🗑️ Remove unused stuff
This commit is contained in:
@@ -1,287 +0,0 @@
|
|||||||
# ActivityPub Implementation for Solar Network
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
This document outlines the initial implementation of ActivityPub federation for the Solar Network (DysonNetwork), following the plan outlined in `ACTIVITYPUB_PLAN.md`.
|
|
||||||
|
|
||||||
## What Has Been Created
|
|
||||||
|
|
||||||
### 1. Database Models (DysonNetwork.Shared/Models)
|
|
||||||
|
|
||||||
All ActivityPub-related models are shared across projects and located in `DysonNetwork.Shared/Models/`:
|
|
||||||
|
|
||||||
#### FediverseInstance.cs
|
|
||||||
- Tracks ActivityPub instances (servers) in the fediverse
|
|
||||||
- Stores instance metadata, blocking status, and activity tracking
|
|
||||||
- Links to actors and content from that instance
|
|
||||||
|
|
||||||
#### FediverseActor.cs
|
|
||||||
- Represents remote actors (users/accounts) from other instances
|
|
||||||
- Stores actor information including keys, inbox/outbox URLs
|
|
||||||
- Links to instance and manages relationships
|
|
||||||
- Tracks whether the actor is a bot, locked, or discoverable
|
|
||||||
|
|
||||||
#### FediverseContent.cs
|
|
||||||
- Stores content (posts, notes, etc.) received from the fediverse
|
|
||||||
- Supports multiple content types (Note, Article, Image, Video, etc.)
|
|
||||||
- Includes attachments, mentions, tags, and emojis
|
|
||||||
- Links to local posts for unified display
|
|
||||||
|
|
||||||
#### FediverseActivity.cs
|
|
||||||
- Tracks ActivityPub activities (Create, Follow, Like, Announce, etc.)
|
|
||||||
- Stores raw activity data and processing status
|
|
||||||
- Links to actors, content, and local entities
|
|
||||||
- Supports both incoming and outgoing activities
|
|
||||||
|
|
||||||
#### FediverseRelationship.cs
|
|
||||||
- Manages follow relationships between local and remote actors
|
|
||||||
- Tracks relationship state (Pending, Accepted, Rejected)
|
|
||||||
- Supports muting and blocking
|
|
||||||
- Links to local accounts/publishers
|
|
||||||
|
|
||||||
#### FediverseReaction.cs
|
|
||||||
- Stores reactions (likes, emoji) from both local and remote actors
|
|
||||||
- Links to content and actor
|
|
||||||
- Supports federation of reactions
|
|
||||||
|
|
||||||
### 2. Database Migration
|
|
||||||
|
|
||||||
**File**: `DysonNetwork.Sphere/Migrations/20251228120000_AddActivityPubModels.cs`
|
|
||||||
|
|
||||||
This migration creates the following tables:
|
|
||||||
- `fediverse_instances` - Instance tracking
|
|
||||||
- `fediverse_actors` - Remote actor profiles
|
|
||||||
- `fediverse_contents` - Federated content storage
|
|
||||||
- `fediverse_activities` - Activity tracking and processing
|
|
||||||
- `fediverse_relationships` - Follow relationships
|
|
||||||
- `fediverse_reactions` - Reactions from fediverse
|
|
||||||
|
|
||||||
### 3. API Controllers (DysonNetwork.Sphere/ActivityPub)
|
|
||||||
|
|
||||||
#### WebFingerController.cs
|
|
||||||
- **Endpoint**: `GET /.well-known/webfinger?resource=acct:<username>@<domain>`
|
|
||||||
- **Purpose**: Allows other instances to discover actors via WebFinger protocol
|
|
||||||
- **Response**: Returns actor's inbox/outbox URLs and profile page links
|
|
||||||
- Maps local Publishers to ActivityPub actors
|
|
||||||
|
|
||||||
#### ActivityPubController.cs
|
|
||||||
Provides three main endpoints:
|
|
||||||
|
|
||||||
1. **GET /activitypub/actors/{username}**
|
|
||||||
- Returns ActivityPub actor profile in JSON-LD format
|
|
||||||
- Includes actor's keys, inbox, outbox, followers, and following URLs
|
|
||||||
- Maps SnPublisher to ActivityPub Person type
|
|
||||||
|
|
||||||
2. **GET /activitypub/actors/{username}/outbox**
|
|
||||||
- Returns actor's outbox collection
|
|
||||||
- Lists public posts as ActivityPub activities
|
|
||||||
- Supports pagination
|
|
||||||
|
|
||||||
3. **POST /activitypub/actors/{username}/inbox**
|
|
||||||
- Receives incoming ActivityPub activities
|
|
||||||
- Supports Create, Follow, Like, Announce activities
|
|
||||||
- Placeholder for activity processing logic
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
### Data Flow
|
|
||||||
|
|
||||||
```
|
|
||||||
Remote Instance Solar Network (Sphere)
|
|
||||||
│ │
|
|
||||||
│ ───WebFinger─────> │
|
|
||||||
│ │
|
|
||||||
│ <───Actor JSON──── │
|
|
||||||
│ │
|
|
||||||
│ ───Activity─────> │ → Inbox Processing
|
|
||||||
│ │
|
|
||||||
│ <───Activity────── │ ← Outbox Distribution
|
|
||||||
```
|
|
||||||
|
|
||||||
### Model Relationships
|
|
||||||
|
|
||||||
- `SnFediverseInstance` has many `SnFediverseActor`
|
|
||||||
- `SnFediverseInstance` has many `SnFediverseContent`
|
|
||||||
- `SnFediverseActor` has many `SnFediverseContent`
|
|
||||||
- `SnFediverseActor` has many `SnFediverseActivity`
|
|
||||||
- `SnFediverseActor` has many `SnFediverseRelationship` (as follower and following)
|
|
||||||
- `SnFediverseContent` has many `SnFediverseActivity`
|
|
||||||
- `SnFediverseContent` has many `SnFediverseReaction`
|
|
||||||
- `SnFediverseContent` optionally links to `SnPost` (local copy)
|
|
||||||
|
|
||||||
### Local to Fediverse Mapping
|
|
||||||
|
|
||||||
| Solar Network Model | ActivityPub Type |
|
|
||||||
|-------------------|-----------------|
|
|
||||||
| SnPublisher | Person (Actor) |
|
|
||||||
| SnPost | Note / Article |
|
|
||||||
| SnPostReaction | Like / EmojiReact |
|
|
||||||
| Follow | Follow Activity |
|
|
||||||
| SnPublisherSubscription | Follow Relationship |
|
|
||||||
|
|
||||||
## Next Steps
|
|
||||||
|
|
||||||
### Stage 1: Core Infrastructure ✅ (COMPLETED)
|
|
||||||
- ✅ Create database models for ActivityPub entities
|
|
||||||
- ✅ Create database migration
|
|
||||||
- ✅ Implement basic WebFinger endpoint
|
|
||||||
- ✅ Implement basic Actor endpoint
|
|
||||||
- ✅ Implement Inbox/Outbox endpoints
|
|
||||||
|
|
||||||
### Stage 2: Activity Processing ✅ (COMPLETED)
|
|
||||||
- ✅ Implement HTTP Signature verification (ActivityPubSignatureService)
|
|
||||||
- ✅ Process incoming activities:
|
|
||||||
- Follow/Accept/Reject
|
|
||||||
- Create (incoming posts)
|
|
||||||
- Like/Announce
|
|
||||||
- Delete/Update
|
|
||||||
- Undo
|
|
||||||
- ✅ Generate outgoing activities (ActivityPubDeliveryService)
|
|
||||||
- ✅ Queue and retry failed deliveries (basic implementation)
|
|
||||||
|
|
||||||
### Stage 3: Key Management ✅ (COMPLETED)
|
|
||||||
- ✅ Generate RSA key pairs for each Publisher (ActivityPubKeyService)
|
|
||||||
- ✅ Store public/private keys in Publisher.Meta
|
|
||||||
- ✅ Sign outgoing HTTP requests
|
|
||||||
- ✅ Verify incoming HTTP signatures
|
|
||||||
|
|
||||||
### Stage 4: Content Federation (IN PROGRESS)
|
|
||||||
- ✅ Convert between SnPost and ActivityPub Note/Article (basic mapping)
|
|
||||||
- ✅ Handle content attachments and media
|
|
||||||
- ✅ Support content warnings and sensitive content
|
|
||||||
- ✅ Handle replies, boosts, and mentions
|
|
||||||
- ⏳ Add local post reference for federated content
|
|
||||||
- ⏳ Handle media attachments in federated content
|
|
||||||
|
|
||||||
### Stage 5: Relationship Management ✅ (COMPLETED)
|
|
||||||
- ✅ Handle follow/unfollow logic
|
|
||||||
- ✅ Update followers/following collections
|
|
||||||
- ✅ Block/mute functionality (data model ready)
|
|
||||||
- ✅ Relationship state machine (Pending, Accepted, Rejected)
|
|
||||||
|
|
||||||
### Stage 6: Testing & Interop (NEXT)
|
|
||||||
- ⏳ Test with Mastodon instances
|
|
||||||
- ⏳ Test with Pleroma/Akkoma instances
|
|
||||||
- ⏳ Test with Lemmy instances
|
|
||||||
- ⏳ Verify WebFinger and actor discovery
|
|
||||||
- ⏳ Test activity delivery and processing
|
|
||||||
|
|
||||||
## Implementation Details
|
|
||||||
|
|
||||||
### Core Services
|
|
||||||
|
|
||||||
#### 1. ActivityPubKeyService
|
|
||||||
- Generates RSA 2048-bit key pairs for ActivityPub
|
|
||||||
- Signs data with private key
|
|
||||||
- Verifies signatures with public key
|
|
||||||
- Key stored in `SnPublisher.Meta["private_key"]` and `["public_key"]`
|
|
||||||
|
|
||||||
#### 2. ActivityPubSignatureService
|
|
||||||
- Verifies incoming HTTP Signature headers
|
|
||||||
- Signs outgoing HTTP requests
|
|
||||||
- Manages key retrieval and storage
|
|
||||||
- Builds signing strings according to ActivityPub spec
|
|
||||||
|
|
||||||
#### 3. ActivityPubActivityProcessor
|
|
||||||
- Processes all incoming activity types
|
|
||||||
- Follow: Creates relationship, sends Accept
|
|
||||||
- Accept: Updates relationship to accepted state
|
|
||||||
- Reject: Updates relationship to rejected state
|
|
||||||
- Create: Stores federated content
|
|
||||||
- Like: Records like reaction
|
|
||||||
- Announce: Increments boost count
|
|
||||||
- Undo: Reverts previous actions
|
|
||||||
- Delete: Soft-deletes federated content
|
|
||||||
- Update: Marks content as edited
|
|
||||||
|
|
||||||
#### 4. ActivityPubDeliveryService
|
|
||||||
- Sends Follow activities to remote instances
|
|
||||||
- Sends Accept activities in response to follows
|
|
||||||
- Sends Create activities (posts) to followers
|
|
||||||
- Sends Like activities to remote instances
|
|
||||||
- Sends Undo activities
|
|
||||||
- Fetches remote actor profiles on-demand
|
|
||||||
|
|
||||||
### Data Flow
|
|
||||||
|
|
||||||
#### Incoming Activity Flow
|
|
||||||
```
|
|
||||||
Remote Server → HTTP Signature Verification → Activity Type → Specific Handler
|
|
||||||
↓
|
|
||||||
Database Update & Response
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Outgoing Activity Flow
|
|
||||||
```
|
|
||||||
Local Action → Create Activity → Sign with Key → Send to Followers' Inboxes
|
|
||||||
↓
|
|
||||||
Track Status & Retry
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
Add to `appsettings.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"ActivityPub": {
|
|
||||||
"Domain": "your-domain.com",
|
|
||||||
"EnableFederation": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Database Migration
|
|
||||||
|
|
||||||
To apply the migration:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd DysonNetwork.Sphere
|
|
||||||
dotnet ef database update
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
### WebFinger
|
|
||||||
```bash
|
|
||||||
curl "https://your-domain.com/.well-known/webfinger?resource=acct:username@your-domain.com"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Actor Profile
|
|
||||||
```bash
|
|
||||||
curl -H "Accept: application/activity+json" https://your-domain.com/activitypub/actors/username
|
|
||||||
```
|
|
||||||
|
|
||||||
### Outbox
|
|
||||||
```bash
|
|
||||||
curl -H "Accept: application/activity+json" https://your-domain.com/activitypub/actors/username/outbox
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- All models follow the existing Solar Network patterns (ModelBase, NodaTime, JSON columns)
|
|
||||||
- Controllers use standard ASP.NET Core patterns with dependency injection
|
|
||||||
- Database uses PostgreSQL with JSONB for flexible metadata storage
|
|
||||||
- Migration follows existing naming conventions
|
|
||||||
- Soft delete is enabled on all models
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- [ActivityPub W3C Recommendation](https://www.w3.org/TR/activitypub/)
|
|
||||||
- [ActivityStreams 2.0](https://www.w3.org/TR/activitystreams-core/)
|
|
||||||
- [WebFinger RFC 7033](https://tools.ietf.org/html/rfc7033)
|
|
||||||
- [Mastodon Federation Documentation](https://docs.joinmastodon.org/spec/activitypub/)
|
|
||||||
|
|
||||||
## TODOs
|
|
||||||
|
|
||||||
- [ ] Implement HTTP Signature verification middleware
|
|
||||||
- [ ] Create activity processor service
|
|
||||||
- [ ] Implement activity queue and retry logic
|
|
||||||
- [ ] Add key generation for Publishers
|
|
||||||
- [ ] Implement content conversion between formats
|
|
||||||
- [ ] Add inbox background worker
|
|
||||||
- [ ] Add outbox delivery worker
|
|
||||||
- [ ] Implement relationship management logic
|
|
||||||
- [ ] Add moderation tools for federated content
|
|
||||||
- [ ] Add federation metrics and monitoring
|
|
||||||
- [ ] Write comprehensive tests
|
|
||||||
@@ -1,197 +0,0 @@
|
|||||||
🛠️ ActivityPub 接入 Solar Network 的分步清单
|
|
||||||
|
|
||||||
⸻
|
|
||||||
|
|
||||||
🧱 1. 准备 & 设计阶段
|
|
||||||
|
|
||||||
1.1 理解 ActivityPub 的核心概念
|
|
||||||
• Actor / Object / Activity / Collection
|
|
||||||
• Outbox / Inbox / Followers 列表
|
|
||||||
ActivityPub 是使用 JSON-LD + ActivityStreams 2.0 来描述社交行为的规范。 
|
|
||||||
|
|
||||||
1.2 映射你现有的 Solar Domain 结构
|
|
||||||
|
|
||||||
把你现在 Solar Network 的用户、帖子、关注、点赞等:
|
|
||||||
• 映射为 ActivityPub 的 Actor / Note / Follow / Like 等
|
|
||||||
• 明确本地模型与 ActivityStreams 对应关系
|
|
||||||
|
|
||||||
比如:
|
|
||||||
• Solar User → ActivityPub Actor
|
|
||||||
• Post → ActivityPub Note/Object
|
|
||||||
• Like → ActivityPub Like Activity
|
|
||||||
这一步是关键的领域建模设计。
|
|
||||||
|
|
||||||
⸻
|
|
||||||
|
|
||||||
🚪 2. Actor 发现与必要入口
|
|
||||||
|
|
||||||
2.1 实现 WebFinger
|
|
||||||
|
|
||||||
为每个用户提供 WebFinger endpoint:
|
|
||||||
|
|
||||||
GET /.well-known/webfinger?resource=acct:<username>@<domain>
|
|
||||||
|
|
||||||
用来让远端服务器查出 actor 细节(包括 inbox/outbox URL)。
|
|
||||||
|
|
||||||
2.2 Actor 资源 URL
|
|
||||||
|
|
||||||
确保每个用户有一个全局可访问的 URL,例如:
|
|
||||||
|
|
||||||
https://solar.io/users/alice
|
|
||||||
|
|
||||||
并在其 JSON-LD 中包含:
|
|
||||||
• inbox
|
|
||||||
• outbox
|
|
||||||
• followers
|
|
||||||
• following
|
|
||||||
这些是 ActivityPub 基础通信的入口。 
|
|
||||||
|
|
||||||
⸻
|
|
||||||
|
|
||||||
📮 3. 核心协议实现
|
|
||||||
|
|
||||||
3.1 Inbox / Outbox 接口
|
|
||||||
|
|
||||||
Inbox(接收来自其他实例的 Activity)
|
|
||||||
Outbox(本地用户发布 Activity 的出口)
|
|
||||||
|
|
||||||
Outbox 需要:
|
|
||||||
• 生成 activity JSON(Create、Follow、Like 等)
|
|
||||||
• 存储至本地数据库
|
|
||||||
• 推送到各 follower 的 Inbox
|
|
||||||
|
|
||||||
Inbox 需要:
|
|
||||||
• 接收并 parse Activity
|
|
||||||
• 验证签名
|
|
||||||
• 处理活动(如接受 Follow,记录远程 Post 等)
|
|
||||||
|
|
||||||
注意:
|
|
||||||
• 请求需要验证 HTTP Signatures(远端服务器签名)。 
|
|
||||||
• 必须满足 ActivityPub 规范对字段的要求。
|
|
||||||
|
|
||||||
⸻
|
|
||||||
|
|
||||||
🔐 4. 安全与签名
|
|
||||||
|
|
||||||
4.1 Actor Keys
|
|
||||||
|
|
||||||
每个 Actor 对应一对 RSA / Ed25519 密钥:
|
|
||||||
• 私钥用于签名发送到其它服务器的请求
|
|
||||||
• 公钥发布在 Actor JSON 中供对方验证
|
|
||||||
|
|
||||||
远端服务器发送到你的 Inbox 时,需要:
|
|
||||||
• 使用对方的公钥验证签名
|
|
||||||
|
|
||||||
HTTP Signatures 是服务器间通信安全的一部分,防止伪造请求。 
|
|
||||||
|
|
||||||
⸻
|
|
||||||
|
|
||||||
🌐 5. 实现联邦逻辑
|
|
||||||
|
|
||||||
5.1 关注逻辑
|
|
||||||
|
|
||||||
处理:
|
|
||||||
• Follow Activity
|
|
||||||
• Accept / Reject Activity
|
|
||||||
• 更新本地 followers / following 数据
|
|
||||||
|
|
||||||
实现流程参考:1. 本地用户发起 Follow 2. 推送 Follow 到远端 Inbox 3. 等待远端发送 Accept 或 Reject
|
|
||||||
|
|
||||||
5.2 推送 content(联邦同步)
|
|
||||||
|
|
||||||
当本地用户发布内容时:
|
|
||||||
• 从 Outbox 取出 Create Activity
|
|
||||||
• 发送到所有远端 followers 的 Inbox
|
|
||||||
注意:你可以缓存远端 followers 数据表来减少重复请求。
|
|
||||||
|
|
||||||
⸻
|
|
||||||
|
|
||||||
📡 6. 消息处理与存储
|
|
||||||
|
|
||||||
6.1 本地对象缓存
|
|
||||||
|
|
||||||
对于接收到的远端内容(Post / Note / Like 等):
|
|
||||||
• 需要保存到 Solar 的数据库
|
|
||||||
• 供 UI / API 生成用户时间线
|
|
||||||
这使得 Solar 能把远端联邦内容与本地内容统一展示。
|
|
||||||
|
|
||||||
6.2 处理 Collections
|
|
||||||
|
|
||||||
ActivityPub 定义了 Collection 类型用于:
|
|
||||||
• followers 列表
|
|
||||||
• liked 列表
|
|
||||||
• outbox、inbox
|
|
||||||
|
|
||||||
你需要实现这些集合的获取与分页逻辑。
|
|
||||||
|
|
||||||
⸻
|
|
||||||
|
|
||||||
🔁 7. 与现有 Solar Network API 协调
|
|
||||||
|
|
||||||
你可能已经有本地的帖子、用户 API。那么:
|
|
||||||
• 把这套 API 与 ActivityPub 同步层绑定
|
|
||||||
• 决定哪些内容对外发布
|
|
||||||
• 决定哪些 Activity 类型需要响应
|
|
||||||
|
|
||||||
比如:
|
|
||||||
|
|
||||||
Solar Post Create -> 生成 ActivityPub Create Note -> 发往联邦
|
|
||||||
|
|
||||||
⸻
|
|
||||||
|
|
||||||
📦 8. 测试与兼容性
|
|
||||||
|
|
||||||
8.1 与现存联邦测试
|
|
||||||
|
|
||||||
用已存在的 ActivityPub 实例测试兼容性:
|
|
||||||
• Mastodon
|
|
||||||
• Pleroma
|
|
||||||
• Lemmy 等
|
|
||||||
|
|
||||||
检查:
|
|
||||||
• 对方是否能关注 Solar 用户
|
|
||||||
• Solar 是否能接收远端内容
|
|
||||||
|
|
||||||
ActivityPub 规范(W3C Recommendation)有详细规范流包括:
|
|
||||||
• Server to Server API
|
|
||||||
你最重要的目标是与现存实例互操作。 
|
|
||||||
|
|
||||||
⸻
|
|
||||||
|
|
||||||
🧪 9. UX & 监控支持
|
|
||||||
|
|
||||||
9.1 用户显示远端内容
|
|
||||||
|
|
||||||
从 Inbox 收到内容后:
|
|
||||||
• 如何展示在 Solar UI
|
|
||||||
• 链接远端用户的展示名 / 头像
|
|
||||||
|
|
||||||
9.2 监控 & 审计
|
|
||||||
• 失败的推送
|
|
||||||
• 无法验证签名的请求
|
|
||||||
• 阻止 spam / 恶意 Activity
|
|
||||||
|
|
||||||
⸻
|
|
||||||
|
|
||||||
🏁 10. 逐步推进
|
|
||||||
|
|
||||||
建议按阶段 rollout:
|
|
||||||
|
|
||||||
阶段 目标
|
|
||||||
Stage 1 实现 Actor / WebFinger / Outbox / Inbox 基本框架
|
|
||||||
Stage 2 支持 Follow / Accept / Reject Activity
|
|
||||||
Stage 3 支持 Create / Like / Announce
|
|
||||||
Stage 4 与远端实例互联测试
|
|
||||||
Stage 5 UI & Feed 统一显示本地 + 联邦内容
|
|
||||||
|
|
||||||
⸻
|
|
||||||
|
|
||||||
📌 小结
|
|
||||||
|
|
||||||
核心步骤总结:1. 映射 Solar Network 数据模型到 ActivityPub 2. 实现 WebFinger + Actor JSON-LD 3. 实现 Inbox 和 Outbox endpoints 4. 管理 Actor Keys 与 HTTP Signatures 5. 处理关注/发帖/点赞等 Activity 6. 推送到远端 / 接收远端同步 7. 将远端内容存入 Solar 并展示 8. 测试与现有 Fediverse 实例互通
|
|
||||||
|
|
||||||
这套步骤覆盖了 ActivityPub 协议必须实现的点和实际联邦要处理的逻辑。 
|
|
||||||
|
|
||||||
⸻
|
|
||||||
|
|
||||||
如果你想,我可以进一步展开 Solar Network 对应的具体 API 设计模板(包括 Inbox / Outbox 的 REST 定义与 JSON 输出示例),甚至帮你写 可运行的 Go / .NET 样例代码。你希望从哪一部分开始深入?
|
|
||||||
@@ -1,273 +0,0 @@
|
|||||||
# ActivityPub Implementation Summary
|
|
||||||
|
|
||||||
## What Has Been Implemented
|
|
||||||
|
|
||||||
### 1. Database Models ✅
|
|
||||||
All models located in `DysonNetwork.Shared/Models/`:
|
|
||||||
|
|
||||||
| Model | Purpose | Key Features |
|
|
||||||
|--------|---------|--------------|
|
|
||||||
| `SnFediverseInstance` | Track fediverse servers | Domain blocking, metadata, activity tracking |
|
|
||||||
| `SnFediverseActor` | Remote user profiles | Keys, inbox/outbox URLs, relationships |
|
|
||||||
| `SnFediverseContent` | Federated posts/notes | Multiple content types, attachments, mentions, tags |
|
|
||||||
| `SnFediverseActivity` | Activity tracking | All activity types, processing status, raw data |
|
|
||||||
| `SnFediverseRelationship` | Follow relationships | State machine, muting/blocking |
|
|
||||||
| `SnFediverseReaction` | Federated reactions | Likes, emoji reactions |
|
|
||||||
|
|
||||||
### 2. Database Migrations ✅
|
|
||||||
- `20251228120000_AddActivityPubModels.cs` - Core ActivityPub tables
|
|
||||||
- `20251228130000_AddPublisherMetaForActivityPubKeys.cs` - Publisher metadata for keys
|
|
||||||
|
|
||||||
### 3. Core Services ✅
|
|
||||||
|
|
||||||
#### ActivityPubKeyService
|
|
||||||
- **Location**: `DysonNetwork.Sphere/ActivityPub/ActivityPubKeyService.cs`
|
|
||||||
- **Responsibilities**:
|
|
||||||
- Generate RSA 2048-bit key pairs
|
|
||||||
- Sign data with private key
|
|
||||||
- Verify signatures with public key
|
|
||||||
- **Key Storage**: Keys stored in `SnPublisher.Meta`
|
|
||||||
|
|
||||||
#### ActivityPubSignatureService
|
|
||||||
- **Location**: `DysonNetwork.Sphere/ActivityPub/ActivityPubSignatureService.cs`
|
|
||||||
- **Responsibilities**:
|
|
||||||
- Verify incoming HTTP Signature headers
|
|
||||||
- Sign outgoing HTTP requests
|
|
||||||
- Build signing strings per ActivityPub spec
|
|
||||||
- Manage key retrieval for actors
|
|
||||||
- **Signature Algorithm**: RSA-SHA256
|
|
||||||
|
|
||||||
#### ActivityPubActivityProcessor
|
|
||||||
- **Location**: `DysonNetwork.Sphere/ActivityPub/ActivityPubActivityProcessor.cs`
|
|
||||||
- **Supported Activities**:
|
|
||||||
- ✅ Follow - Creates relationship, sends Accept
|
|
||||||
- ✅ Accept - Updates relationship to accepted
|
|
||||||
- ✅ Reject - Updates relationship to rejected
|
|
||||||
- ✅ Create - Stores federated content
|
|
||||||
- ✅ Like - Records like reaction
|
|
||||||
- ✅ Announce - Increments boost count
|
|
||||||
- ✅ Undo - Reverts previous actions
|
|
||||||
- ✅ Delete - Soft-deletes federated content
|
|
||||||
- ✅ Update - Marks content as edited
|
|
||||||
|
|
||||||
#### ActivityPubDeliveryService
|
|
||||||
- **Location**: `DysonNetwork.Sphere/ActivityPub/ActivityPubDeliveryService.cs`
|
|
||||||
- **Outgoing Activities**:
|
|
||||||
- ✅ Follow - Send to remote actors
|
|
||||||
- ✅ Accept - Respond to follow requests
|
|
||||||
- ✅ Create - Send new posts to followers
|
|
||||||
- ✅ Like - Send to remote instances
|
|
||||||
- ✅ Undo - Undo previous actions
|
|
||||||
- **Features**:
|
|
||||||
- HTTP signature signing
|
|
||||||
- Remote actor fetching
|
|
||||||
- Follower discovery
|
|
||||||
|
|
||||||
### 4. API Controllers ✅
|
|
||||||
|
|
||||||
#### WebFingerController
|
|
||||||
- **Location**: `DysonNetwork.Sphere/ActivityPub/WebFingerController.cs`
|
|
||||||
- **Endpoints**:
|
|
||||||
- `GET /.well-known/webfinger?resource=acct:<username>@<domain>`
|
|
||||||
- **Purpose**: Allow remote instances to discover local actors
|
|
||||||
|
|
||||||
#### ActivityPubController
|
|
||||||
- **Location**: `DysonNetwork.Sphere/ActivityPub/ActivityPubController.cs`
|
|
||||||
- **Endpoints**:
|
|
||||||
- `GET /activitypub/actors/{username}` - Actor profile in JSON-LD
|
|
||||||
- `GET /activitypub/actors/{username}/outbox` - Public posts
|
|
||||||
- `POST /activitypub/actors/{username}/inbox` - Receive activities
|
|
||||||
- **Features**:
|
|
||||||
- Public key in actor profile
|
|
||||||
- ActivityPub JSON-LD responses
|
|
||||||
- HTTP signature verification on inbox
|
|
||||||
- Activity processing pipeline
|
|
||||||
|
|
||||||
### 5. Model Updates ✅
|
|
||||||
- Added `Meta` field to `SnPublisher` for storing ActivityPub keys
|
|
||||||
- All models follow existing Solar Network patterns
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
### Incoming Activity Flow
|
|
||||||
```
|
|
||||||
1. Remote server sends POST to /inbox
|
|
||||||
2. HTTP Signature is verified
|
|
||||||
3. Activity type is identified
|
|
||||||
4. Specific handler processes activity:
|
|
||||||
- Follow: Create relationship, send Accept
|
|
||||||
- Create: Store content
|
|
||||||
- Like: Record reaction
|
|
||||||
- etc.
|
|
||||||
5. Database is updated
|
|
||||||
6. Response sent
|
|
||||||
```
|
|
||||||
|
|
||||||
### Outgoing Activity Flow
|
|
||||||
```
|
|
||||||
1. Local action occurs (post, like, follow)
|
|
||||||
2. Activity is created in ActivityPub format
|
|
||||||
3. Remote followers are discovered
|
|
||||||
4. HTTP request is signed with publisher's private key
|
|
||||||
5. Activity sent to each follower's inbox
|
|
||||||
6. Status logged
|
|
||||||
```
|
|
||||||
|
|
||||||
### Key Management
|
|
||||||
```
|
|
||||||
1. Publisher creates post/follows
|
|
||||||
2. Check if keys exist in Publisher.Meta
|
|
||||||
3. If not, generate RSA 2048-bit key pair
|
|
||||||
4. Store keys in Publisher.Meta
|
|
||||||
5. Use keys for signing
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
Add to `appsettings.json`:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"ActivityPub": {
|
|
||||||
"Domain": "your-domain.com",
|
|
||||||
"EnableFederation": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## API Endpoints
|
|
||||||
|
|
||||||
### WebFinger
|
|
||||||
```bash
|
|
||||||
GET /.well-known/webfinger?resource=acct:username@domain.com
|
|
||||||
Accept: application/jrd+json
|
|
||||||
```
|
|
||||||
|
|
||||||
### Actor Profile
|
|
||||||
```bash
|
|
||||||
GET /activitypub/actors/username
|
|
||||||
Accept: application/activity+json
|
|
||||||
```
|
|
||||||
|
|
||||||
### Outbox
|
|
||||||
```bash
|
|
||||||
GET /activitypub/actors/username/outbox
|
|
||||||
Accept: application/activity+json
|
|
||||||
```
|
|
||||||
|
|
||||||
### Inbox
|
|
||||||
```bash
|
|
||||||
POST /activitypub/actors/username/inbox
|
|
||||||
Content-Type: application/activity+json
|
|
||||||
Signature: keyId="...",algorithm="...",headers="...",signature="..."
|
|
||||||
```
|
|
||||||
|
|
||||||
## Database Schema
|
|
||||||
|
|
||||||
### Fediverse Tables
|
|
||||||
- `fediverse_instances` - Server metadata and blocking
|
|
||||||
- `fediverse_actors` - Remote actor profiles
|
|
||||||
- `fediverse_contents` - Federated posts/notes
|
|
||||||
- `fediverse_activities` - Activity tracking
|
|
||||||
- `fediverse_relationships` - Follow relationships
|
|
||||||
- `fediverse_reactions` - Federated reactions
|
|
||||||
|
|
||||||
### Publisher Enhancement
|
|
||||||
- Added `publishers.meta` JSONB column for key storage
|
|
||||||
|
|
||||||
## Next Steps
|
|
||||||
|
|
||||||
### Immediate (Ready for Testing)
|
|
||||||
- Apply database migrations
|
|
||||||
- Test WebFinger with a Mastodon instance
|
|
||||||
- Test follow/unfollow with another instance
|
|
||||||
- Test receiving posts from federated timeline
|
|
||||||
|
|
||||||
### Short Term
|
|
||||||
- Add HTTP Signature verification middleware
|
|
||||||
- Implement activity queue with retry logic
|
|
||||||
- Add background worker for processing queued activities
|
|
||||||
- Add metrics and monitoring
|
|
||||||
- Implement local content display in timelines
|
|
||||||
|
|
||||||
### Long Term
|
|
||||||
- Add Media support for federated content
|
|
||||||
- Implement content filtering
|
|
||||||
- Add moderation tools for federated content
|
|
||||||
- Support more activity types
|
|
||||||
- Implement instance block list management
|
|
||||||
|
|
||||||
## Compatibility
|
|
||||||
|
|
||||||
The implementation follows:
|
|
||||||
- ✅ [ActivityPub W3C Recommendation](https://www.w3.org/TR/activitypub/)
|
|
||||||
- ✅ [ActivityStreams 2.0](https://www.w3.org/TR/activitystreams-core/)
|
|
||||||
- ✅ [WebFinger RFC 7033](https://tools.ietf.org/html/rfc7033)
|
|
||||||
- ✅ [HTTP Signatures](https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures)
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
### Local Testing
|
|
||||||
```bash
|
|
||||||
# 1. Apply migrations
|
|
||||||
cd DysonNetwork.Sphere
|
|
||||||
dotnet ef database update
|
|
||||||
|
|
||||||
# 2. Test WebFinger
|
|
||||||
curl "http://localhost:5000/.well-known/webfinger?resource=acct:username@localhost"
|
|
||||||
|
|
||||||
# 3. Test Actor
|
|
||||||
curl -H "Accept: application/activity+json" http://localhost:5000/activitypub/actors/username
|
|
||||||
```
|
|
||||||
|
|
||||||
### Federation Testing
|
|
||||||
1. Set up a Mastodon instance (or use a public one)
|
|
||||||
2. Follow a Mastodon user from Solar Network
|
|
||||||
3. Create a post on Solar Network
|
|
||||||
4. Verify it appears on Mastodon timeline
|
|
||||||
|
|
||||||
## Architecture Decisions
|
|
||||||
|
|
||||||
1. **Key Storage**: Using `SnPublisher.Meta` JSONB field for flexibility
|
|
||||||
2. **Content Storage**: Federated content stored separately from local posts
|
|
||||||
3. **Relationship State**: Implemented with explicit states (Pending, Accepted, Rejected)
|
|
||||||
4. **Signature Algorithm**: RSA-SHA256 for compatibility
|
|
||||||
5. **Activity Processing**: Synchronous for now, can be made async with queue
|
|
||||||
6. **Content Types**: Support for Note, Article initially (can expand)
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- All ActivityPub communication uses HTTP Signatures
|
|
||||||
- Private keys never leave the server
|
|
||||||
- Public keys are published in actor profiles
|
|
||||||
- Soft delete is enabled on all federated models
|
|
||||||
- Failed activity deliveries are logged but not retried (future enhancement)
|
|
||||||
- Content is federated only when visibility is Public
|
|
||||||
|
|
||||||
## Files Created/Modified
|
|
||||||
|
|
||||||
### New Files
|
|
||||||
- `DysonNetwork.Shared/Models/FediverseInstance.cs`
|
|
||||||
- `DysonNetwork.Shared/Models/FediverseActor.cs`
|
|
||||||
- `DysonNetwork.Shared/Models/FediverseContent.cs`
|
|
||||||
- `DysonNetwork.Shared/Models/FediverseActivity.cs`
|
|
||||||
- `DysonNetwork.Shared/Models/FediverseRelationship.cs`
|
|
||||||
- `DysonNetwork.Shared/Models/FediverseReaction.cs`
|
|
||||||
- `DysonNetwork.Sphere/ActivityPub/WebFingerController.cs`
|
|
||||||
- `DysonNetwork.Sphere/ActivityPub/ActivityPubController.cs`
|
|
||||||
- `DysonNetwork.Sphere/ActivityPub/ActivityPubKeyService.cs`
|
|
||||||
- `DysonNetwork.Sphere/ActivityPub/ActivityPubSignatureService.cs`
|
|
||||||
- `DysonNetwork.Sphere/ActivityPub/ActivityPubActivityProcessor.cs`
|
|
||||||
- `DysonNetwork.Sphere/ActivityPub/ActivityPubDeliveryService.cs`
|
|
||||||
- `DysonNetwork.Sphere/Migrations/20251228120000_AddActivityPubModels.cs`
|
|
||||||
- `DysonNetwork.Sphere/Migrations/20251228130000_AddPublisherMetaForActivityPubKeys.cs`
|
|
||||||
|
|
||||||
### Modified Files
|
|
||||||
- `DysonNetwork.Shared/Models/Publisher.cs` - Added Meta field
|
|
||||||
- `DysonNetwork.Sphere/AppDatabase.cs` - Added DbSets for ActivityPub
|
|
||||||
- `DysonNetwork.Sphere/Startup/ServiceCollectionExtensions.cs` - Registered ActivityPub services
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- [ActivityPub Implementation Guide](./ACTIVITYPUB_IMPLEMENTATION.md)
|
|
||||||
- [ActivityPub Plan](./ACTIVITYPUB_PLAN.md)
|
|
||||||
- [Solar Network Architecture](./README.md)
|
|
||||||
@@ -1,820 +0,0 @@
|
|||||||
# ActivityPub Testing Guide for Solar Network
|
|
||||||
|
|
||||||
This guide will help you test the ActivityPub implementation in Solar Network, starting with a self-hosted instance and then moving to a real instance.
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
- ✅ Solar Network codebase with ActivityPub implementation
|
|
||||||
- ✅ Docker installed (for running Mastodon/Fediverse instances)
|
|
||||||
- ✅ PostgreSQL database running
|
|
||||||
- ✅ `.NET 10` SDK
|
|
||||||
|
|
||||||
## Part 1: Set Up a Self-Hosted Test Instance
|
|
||||||
|
|
||||||
### Option A: Using Mastodon (Recommended for Compatibility)
|
|
||||||
|
|
||||||
#### 1. Create a Docker Compose File
|
|
||||||
|
|
||||||
Create `docker-compose.mastodon-test.yml`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
|
||||||
db:
|
|
||||||
restart: always
|
|
||||||
image: postgres:14-alpine
|
|
||||||
environment:
|
|
||||||
POSTGRES_USER: mastodon
|
|
||||||
POSTGRES_PASSWORD: mastodon_password
|
|
||||||
POSTGRES_DB: mastodon
|
|
||||||
networks:
|
|
||||||
- mastodon_network
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "pg_isready", "-U", "mastodon"]
|
|
||||||
interval: 5s
|
|
||||||
retries: 5
|
|
||||||
|
|
||||||
redis:
|
|
||||||
restart: always
|
|
||||||
image: redis:7-alpine
|
|
||||||
networks:
|
|
||||||
- mastodon_network
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "redis-cli", "ping"]
|
|
||||||
interval: 5s
|
|
||||||
retries: 5
|
|
||||||
|
|
||||||
es:
|
|
||||||
restart: always
|
|
||||||
image: docker.elastic.co/elasticsearch:8.10.2
|
|
||||||
environment:
|
|
||||||
- "discovery.type=single-node"
|
|
||||||
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
|
|
||||||
- "xpack.security.enabled=false"
|
|
||||||
networks:
|
|
||||||
- mastodon_network
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "curl -silent http://localhost:9200/_cluster/health || exit 1"]
|
|
||||||
interval: 10s
|
|
||||||
retries: 10
|
|
||||||
|
|
||||||
web:
|
|
||||||
restart: always
|
|
||||||
image: tootsuite/mastodon:latest
|
|
||||||
env_file: .env.mastodon
|
|
||||||
command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000"
|
|
||||||
ports:
|
|
||||||
- "3001:3000"
|
|
||||||
depends_on:
|
|
||||||
- db
|
|
||||||
- redis
|
|
||||||
- es
|
|
||||||
networks:
|
|
||||||
- mastodon_network
|
|
||||||
volumes:
|
|
||||||
- ./mastodon-data/public:/mastodon/public/system
|
|
||||||
|
|
||||||
streaming:
|
|
||||||
restart: always
|
|
||||||
image: tootsuite/mastodon:latest
|
|
||||||
env_file: .env.mastodon
|
|
||||||
command: node ./streaming
|
|
||||||
ports:
|
|
||||||
- "4000:4000"
|
|
||||||
depends_on:
|
|
||||||
- db
|
|
||||||
- redis
|
|
||||||
networks:
|
|
||||||
- mastodon_network
|
|
||||||
|
|
||||||
sidekiq:
|
|
||||||
restart: always
|
|
||||||
image: tootsuite/mastodon:latest
|
|
||||||
env_file: .env.mastodon
|
|
||||||
command: bundle exec sidekiq
|
|
||||||
depends_on:
|
|
||||||
- db
|
|
||||||
- redis
|
|
||||||
networks:
|
|
||||||
- mastodon_network
|
|
||||||
|
|
||||||
networks:
|
|
||||||
mastodon_network:
|
|
||||||
driver: bridge
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2. Create Environment File
|
|
||||||
|
|
||||||
Create `.env.mastodon`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Federation
|
|
||||||
LOCAL_DOMAIN=mastodon.local
|
|
||||||
LOCAL_HTTPS=false
|
|
||||||
|
|
||||||
# Database
|
|
||||||
DB_HOST=db
|
|
||||||
DB_PORT=5432
|
|
||||||
DB_USER=mastodon
|
|
||||||
DB_NAME=mastodon
|
|
||||||
DB_PASS=mastodon_password
|
|
||||||
|
|
||||||
# Redis
|
|
||||||
REDIS_HOST=redis
|
|
||||||
REDIS_PORT=6379
|
|
||||||
|
|
||||||
# Elasticsearch
|
|
||||||
ES_ENABLED=true
|
|
||||||
ES_HOST=es
|
|
||||||
ES_PORT=9200
|
|
||||||
|
|
||||||
# Secrets (generate these!)
|
|
||||||
SECRET_KEY_BASE=change_me_to_a_random_string_at_least_32_chars
|
|
||||||
OTP_SECRET=change_me_to_another_random_string
|
|
||||||
|
|
||||||
# Defaults
|
|
||||||
SINGLE_USER_MODE=false
|
|
||||||
DEFAULT_LOCALE=en
|
|
||||||
```
|
|
||||||
|
|
||||||
**Generate secrets:**
|
|
||||||
```bash
|
|
||||||
# Run these to generate random secrets
|
|
||||||
openssl rand -base64 32
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 3. Start Mastodon
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker-compose -f docker-compose.mastodon-test.yml up -d
|
|
||||||
|
|
||||||
# Check logs
|
|
||||||
docker-compose -f docker-compose.mastodon-test.yml logs -f web
|
|
||||||
```
|
|
||||||
|
|
||||||
Wait for the web service to be healthy (may take 2-5 minutes).
|
|
||||||
|
|
||||||
#### 4. Create a Mastodon Account
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Run this command to create an admin account
|
|
||||||
docker-compose -f docker-compose.mastodon-test.yml exec web \
|
|
||||||
bin/tootctl accounts create \
|
|
||||||
testuser \
|
|
||||||
testuser@mastodon.local \
|
|
||||||
--email=test@example.com \
|
|
||||||
--confirmed \
|
|
||||||
--role=admin \
|
|
||||||
--approve
|
|
||||||
```
|
|
||||||
|
|
||||||
Set password: `TestPassword123!`
|
|
||||||
|
|
||||||
#### 5. Update Your /etc/hosts
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo nano /etc/hosts
|
|
||||||
```
|
|
||||||
|
|
||||||
Add:
|
|
||||||
```
|
|
||||||
127.0.0.1 mastodon.local
|
|
||||||
127.0.0.1 solar.local
|
|
||||||
```
|
|
||||||
|
|
||||||
### Option B: Using GoToSocial (Lightweight Alternative)
|
|
||||||
|
|
||||||
Create `docker-compose.gotosocial.yml`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
|
||||||
gotosocial:
|
|
||||||
image: superseriousbusiness/gotosocial:latest
|
|
||||||
environment:
|
|
||||||
- GTS_HOST=gotosocial.local
|
|
||||||
- GTS_ACCOUNT_DOMAIN=gotosocial.local
|
|
||||||
- GTS_PROTOCOL=http
|
|
||||||
- GTS_DB_TYPE=sqlite
|
|
||||||
- GTS_DB_ADDRESS=/gotosocial/data/sqlite.db
|
|
||||||
- GTS_STORAGE_LOCAL_BASE_PATH=/gotosocial/data/storage
|
|
||||||
ports:
|
|
||||||
- "3002:8080"
|
|
||||||
volumes:
|
|
||||||
- ./gotosocial-data:/gotosocial/data
|
|
||||||
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
```
|
|
||||||
|
|
||||||
Start it:
|
|
||||||
```bash
|
|
||||||
docker-compose -f docker-compose.gotosocial.yml up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
Create account:
|
|
||||||
```bash
|
|
||||||
docker-compose -f docker-compose.gotosocial.yml exec gotosocial \
|
|
||||||
/gotosocial/gotosocial admin account create \
|
|
||||||
--username testuser \
|
|
||||||
--email test@example.com \
|
|
||||||
--password TestPassword123!
|
|
||||||
```
|
|
||||||
|
|
||||||
## Part 2: Configure Solar Network for Federation
|
|
||||||
|
|
||||||
### 1. Update appsettings.json
|
|
||||||
|
|
||||||
Edit `DysonNetwork.Sphere/appsettings.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"ActivityPub": {
|
|
||||||
"Domain": "solar.local",
|
|
||||||
"EnableFederation": true
|
|
||||||
},
|
|
||||||
"Kestrel": {
|
|
||||||
"Endpoints": {
|
|
||||||
"Http": {
|
|
||||||
"Url": "http://solar.local:5000"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Update /etc/hosts
|
|
||||||
|
|
||||||
Add both instances:
|
|
||||||
```
|
|
||||||
127.0.0.1 mastodon.local
|
|
||||||
127.0.0.1 solar.local
|
|
||||||
127.0.0.1 gotosocial.local
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Apply Database Migrations
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd DysonNetwork.Sphere
|
|
||||||
dotnet ef database update
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Start Solar Network
|
|
||||||
|
|
||||||
```bash
|
|
||||||
dotnet run --project DysonNetwork.Sphere
|
|
||||||
```
|
|
||||||
|
|
||||||
Solar Network should now be running on `http://solar.local:5000`
|
|
||||||
|
|
||||||
## Part 3: Create Test Users
|
|
||||||
|
|
||||||
### In Solar Network
|
|
||||||
|
|
||||||
1. Open http://solar.local:5000 (or your web interface)
|
|
||||||
2. Create a new account/publisher named `solaruser`
|
|
||||||
3. Note down the publisher ID for later
|
|
||||||
|
|
||||||
**Or via API** (if you have an existing account):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# First, create a publisher in Solar Network
|
|
||||||
curl -X POST http://solar.local:5000/api/publishers \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-H "Authorization: Bearer YOUR_TOKEN" \
|
|
||||||
-d '{
|
|
||||||
"name": "solaruser",
|
|
||||||
"nick": "Solar User",
|
|
||||||
"bio": "Testing ActivityPub federation!",
|
|
||||||
"type": 0
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
### In Mastodon
|
|
||||||
|
|
||||||
Open http://mastodon.local:3001 and log in with:
|
|
||||||
- Username: `testuser`
|
|
||||||
- Password: `TestPassword123!`
|
|
||||||
|
|
||||||
## Part 4: Test Federation Scenarios
|
|
||||||
|
|
||||||
### Test 1: WebFinger Discovery
|
|
||||||
|
|
||||||
**Goal**: Verify Solar Network is discoverable
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Query Solar Network's WebFinger endpoint
|
|
||||||
curl -v "http://solar.local:5000/.well-known/webfinger?resource=acct:solaruser@solar.local"
|
|
||||||
|
|
||||||
# Expected response (200 OK):
|
|
||||||
{
|
|
||||||
"subject": "acct:solaruser@solar.local",
|
|
||||||
"links": [
|
|
||||||
{
|
|
||||||
"rel": "self",
|
|
||||||
"type": "application/activity+json",
|
|
||||||
"href": "https://solar.local:5000/activitypub/actors/solaruser"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rel": "http://webfinger.net/rel/profile-page",
|
|
||||||
"type": "text/html",
|
|
||||||
"href": "https://solar.local:5000/users/solaruser"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test 2: Fetch Actor Profile
|
|
||||||
|
|
||||||
**Goal**: Get ActivityPub actor JSON
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Fetch Solar Network actor from Mastodon
|
|
||||||
curl -H "Accept: application/activity+json" \
|
|
||||||
http://solar.local:5000/activitypub/actors/solaruser
|
|
||||||
|
|
||||||
# Expected response includes:
|
|
||||||
{
|
|
||||||
"@context": ["https://www.w3.org/ns/activitystreams"],
|
|
||||||
"id": "https://solar.local:5000/activitypub/actors/solaruser",
|
|
||||||
"type": "Person",
|
|
||||||
"preferredUsername": "solaruser",
|
|
||||||
"inbox": "https://solar.local:5000/activitypub/actors/solaruser/inbox",
|
|
||||||
"outbox": "https://solar.local:5000/activitypub/actors/solaruser/outbox",
|
|
||||||
"followers": "https://solar.local:5000/activitypub/actors/solaruser/followers",
|
|
||||||
"publicKey": {
|
|
||||||
"id": "https://solar.local:5000/activitypub/actors/solaruser#main-key",
|
|
||||||
"owner": "https://solar.local:5000/activitypub/actors/solaruser",
|
|
||||||
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\n..."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test 3: Follow from Mastodon to Solar Network
|
|
||||||
|
|
||||||
**Goal**: Mastodon user follows Solar Network user
|
|
||||||
|
|
||||||
1. **In Mastodon**:
|
|
||||||
- Go to http://mastodon.local:3001
|
|
||||||
- In search bar, type: `@solaruser@solar.local`
|
|
||||||
- Click the follow button
|
|
||||||
|
|
||||||
2. **Verify in Solar Network**:
|
|
||||||
```bash
|
|
||||||
# Check database for relationship
|
|
||||||
psql -d dyson_network -c \
|
|
||||||
"SELECT * FROM fediverse_relationships WHERE is_local_actor = true;"
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Check Solar Network logs**:
|
|
||||||
Should see:
|
|
||||||
```
|
|
||||||
Processing activity type: Follow from actor: ...
|
|
||||||
Processed follow from ... to ...
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Verify Mastodon receives Accept**:
|
|
||||||
- Check Mastodon logs for Accept activity
|
|
||||||
- Verify follow appears as accepted in Mastodon
|
|
||||||
|
|
||||||
### Test 4: Follow from Solar Network to Mastodon
|
|
||||||
|
|
||||||
**Goal**: Solar Network user follows Mastodon user
|
|
||||||
|
|
||||||
You'll need to call the ActivityPub delivery service:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Via API (you'll need to implement this endpoint):
|
|
||||||
curl -X POST http://solar.local:5000/api/activitypub/follow \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-H "Authorization: Bearer YOUR_TOKEN" \
|
|
||||||
-d '{
|
|
||||||
"targetActorUri": "http://mastodon.local:3001/users/testuser"
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
**Or test directly with curl** (simulating a Follow activity):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Create a Follow activity
|
|
||||||
curl -X POST http://solar.local:5000/activitypub/actors/solaruser/inbox \
|
|
||||||
-H "Content-Type: application/activity+json" \
|
|
||||||
-d '{
|
|
||||||
"@context": "https://www.w3.org/ns/activitystreams",
|
|
||||||
"id": "http://mastodon.local:3001/test-follow-activity",
|
|
||||||
"type": "Follow",
|
|
||||||
"actor": "http://mastodon.local:3001/users/testuser",
|
|
||||||
"object": "https://solar.local:5000/activitypub/actors/solaruser"
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test 5: Create a Post in Solar Network
|
|
||||||
|
|
||||||
**Goal**: Post federates to Mastodon
|
|
||||||
|
|
||||||
1. **Create a post via Solar Network API**:
|
|
||||||
```bash
|
|
||||||
curl -X POST http://solar.local:5000/api/posts \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-H "Authorization: Bearer YOUR_TOKEN" \
|
|
||||||
-d '{
|
|
||||||
"content": "Hello fediverse! Testing ActivityPub from Solar Network! 🚀",
|
|
||||||
"visibility": 0,
|
|
||||||
"publisherId": "PUBLISHER_ID"
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Wait a few seconds**
|
|
||||||
|
|
||||||
3. **Check in Mastodon**:
|
|
||||||
- Go to http://mastodon.local:3001
|
|
||||||
- The post should appear in the federated timeline
|
|
||||||
- It should show `@solaruser@solar.local` as the author
|
|
||||||
|
|
||||||
4. **Verify Solar Network logs**:
|
|
||||||
```
|
|
||||||
Successfully sent activity to http://mastodon.local:3001/inbox
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test 6: Like from Mastodon
|
|
||||||
|
|
||||||
**Goal**: Mastodon user likes a Solar Network post
|
|
||||||
|
|
||||||
1. **In Mastodon**:
|
|
||||||
- Find the Solar Network post
|
|
||||||
- Click the favorite/like button
|
|
||||||
|
|
||||||
2. **Verify in Solar Network**:
|
|
||||||
```bash
|
|
||||||
psql -d dyson_network -c \
|
|
||||||
"SELECT * FROM fediverse_reactions;"
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Check Solar Network logs**:
|
|
||||||
```
|
|
||||||
Processing activity type: Like from actor: ...
|
|
||||||
Processed like from ...
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test 7: Reply from Mastodon
|
|
||||||
|
|
||||||
**Goal**: Reply federates to Solar Network
|
|
||||||
|
|
||||||
1. **In Mastodon**:
|
|
||||||
- Reply to the Solar Network post
|
|
||||||
- Write: "@solaruser Nice to meet you!"
|
|
||||||
|
|
||||||
2. **Verify in Solar Network**:
|
|
||||||
```bash
|
|
||||||
psql -d dyson_network -c \
|
|
||||||
"SELECT * FROM fediverse_contents WHERE in_reply_to IS NOT NULL;"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Part 5: Debugging and Troubleshooting
|
|
||||||
|
|
||||||
### Enable Detailed Logging
|
|
||||||
|
|
||||||
Edit `appsettings.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Debug",
|
|
||||||
"DysonNetwork.Sphere.ActivityPub": "Trace"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Check Database State
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check actors
|
|
||||||
psql -d dyson_network -c \
|
|
||||||
"SELECT uri, username, display_name FROM fediverse_actors;"
|
|
||||||
|
|
||||||
# Check contents
|
|
||||||
psql -d dyson_network -c \
|
|
||||||
"SELECT uri, type, content FROM fediverse_contents;"
|
|
||||||
|
|
||||||
# Check relationships
|
|
||||||
psql -d dyson_network -c \
|
|
||||||
"SELECT * FROM fediverse_relationships;"
|
|
||||||
|
|
||||||
# Check activities
|
|
||||||
psql -d dyson_network -c \
|
|
||||||
"SELECT type, status, error_message FROM fediverse_activities;"
|
|
||||||
|
|
||||||
# Check failed activities
|
|
||||||
psql -d dyson_network -c \
|
|
||||||
"SELECT * FROM fediverse_activities WHERE status = 3;" # 3 = Failed
|
|
||||||
```
|
|
||||||
|
|
||||||
### Common Issues
|
|
||||||
|
|
||||||
#### Issue: "Failed to verify signature"
|
|
||||||
|
|
||||||
**Cause**: HTTP Signature verification failed
|
|
||||||
|
|
||||||
**Solutions**:
|
|
||||||
1. Check the signature header format
|
|
||||||
2. Verify public key matches actor's keyId
|
|
||||||
3. Ensure Date header is within 5 minutes
|
|
||||||
4. Check host header matches request URL
|
|
||||||
|
|
||||||
#### Issue: "Target actor or inbox not found"
|
|
||||||
|
|
||||||
**Cause**: Remote actor not fetched yet
|
|
||||||
|
|
||||||
**Solutions**:
|
|
||||||
1. Manually fetch the actor first
|
|
||||||
2. Check actor URL is correct
|
|
||||||
3. Verify remote instance is accessible
|
|
||||||
|
|
||||||
#### Issue: "Content already exists"
|
|
||||||
|
|
||||||
**Cause**: Duplicate activity received
|
|
||||||
|
|
||||||
**Solutions**:
|
|
||||||
1. This is normal - deduplication is working
|
|
||||||
2. Check if content appears correctly
|
|
||||||
|
|
||||||
#### Issue: CORS errors when testing from browser
|
|
||||||
|
|
||||||
**Cause**: Browser blocking cross-origin requests
|
|
||||||
|
|
||||||
**Solutions**:
|
|
||||||
1. Use curl for API testing
|
|
||||||
2. Or disable CORS in development
|
|
||||||
3. Test directly from Mastodon interface
|
|
||||||
|
|
||||||
### View HTTP Signatures
|
|
||||||
|
|
||||||
For debugging, you can inspect the signature:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# From Mastodon to Solar Network
|
|
||||||
curl -v -X POST http://solar.local:5000/activitypub/actors/solaruser/inbox \
|
|
||||||
-H "Content-Type: application/activity+json" \
|
|
||||||
-d '{"type":"Follow",...}'
|
|
||||||
```
|
|
||||||
|
|
||||||
Look for the `Signature` header in the output.
|
|
||||||
|
|
||||||
### Test HTTP Signature Verification Manually
|
|
||||||
|
|
||||||
Create a test script `test-signature.js`:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const crypto = require('crypto');
|
|
||||||
|
|
||||||
// Test signature verification
|
|
||||||
const publicKey = `-----BEGIN PUBLIC KEY-----
|
|
||||||
...
|
|
||||||
-----END PUBLIC KEY-----`;
|
|
||||||
|
|
||||||
const signingString = `(request-target): post /activitypub/actors/solaruser/inbox
|
|
||||||
host: solar.local:5000
|
|
||||||
date: ${new Date().toUTCString()}
|
|
||||||
content-length: ...`;
|
|
||||||
|
|
||||||
const signature = '...';
|
|
||||||
|
|
||||||
const verify = crypto.createVerify('SHA256');
|
|
||||||
verify.update(signingString);
|
|
||||||
const isValid = verify.verify(publicKey, signature, 'base64');
|
|
||||||
|
|
||||||
console.log('Signature valid:', isValid);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Part 6: Test with a Real Instance
|
|
||||||
|
|
||||||
### Preparing for Public Federation
|
|
||||||
|
|
||||||
1. **Get a real domain** (e.g., via ngrok or a VPS)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Using ngrok for testing
|
|
||||||
ngrok http 5000
|
|
||||||
|
|
||||||
# This gives you: https://random-id.ngrok-free.app
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Update Solar Network config**:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"ActivityPub": {
|
|
||||||
"Domain": "your-domain.com",
|
|
||||||
"EnableFederation": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Update DNS** (if using real domain):
|
|
||||||
- Add A record pointing to your server
|
|
||||||
- Configure HTTPS (required for production federation)
|
|
||||||
|
|
||||||
4. **Test WebFinger with your domain**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl "https://your-domain.com/.well-known/webfinger?resource=acct:username@your-domain.com"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test with Mastodon.social
|
|
||||||
|
|
||||||
1. **Create a Mastodon.social account**
|
|
||||||
- Go to https://mastodon.social
|
|
||||||
- Sign up for a test account
|
|
||||||
|
|
||||||
2. **Search for your Solar Network user**:
|
|
||||||
- In Mastodon.social search: `@username@your-domain.com`
|
|
||||||
- Click follow
|
|
||||||
|
|
||||||
3. **Create a post in Solar Network**
|
|
||||||
- Should appear in Mastodon.social
|
|
||||||
|
|
||||||
4. **Reply from Mastodon.social**
|
|
||||||
- Should appear in Solar Network
|
|
||||||
|
|
||||||
### Test with Other Instances
|
|
||||||
|
|
||||||
- **Pleroma**: Similar to Mastodon, good for testing
|
|
||||||
- **Lemmy**: For testing community features (later)
|
|
||||||
- **Pixelfed**: For testing media posts
|
|
||||||
- **PeerTube**: For testing video content (later)
|
|
||||||
|
|
||||||
## Part 7: Verification Checklist
|
|
||||||
|
|
||||||
### Self-Hosted Instance Tests
|
|
||||||
|
|
||||||
- [ ] WebFinger returns correct actor links
|
|
||||||
- [ ] Actor profile has all required fields
|
|
||||||
- [ ] Follow from Mastodon to Solar Network works
|
|
||||||
- [ ] Follow from Solar Network to Mastodon works
|
|
||||||
- [ ] Accept activity sent back to Mastodon
|
|
||||||
- [ ] Posts from Solar Network appear in Mastodon timeline
|
|
||||||
- [ ] Posts from Mastodon appear in Solar Network database
|
|
||||||
- [ ] Likes from Mastodon appear in Solar Network
|
|
||||||
- [ ] Replies from Mastodon appear in Solar Network
|
|
||||||
- [ ] Keys are properly generated and stored
|
|
||||||
- [ ] HTTP signatures are correctly verified
|
|
||||||
- [ ] Outbox returns public posts
|
|
||||||
|
|
||||||
### Real Instance Tests
|
|
||||||
|
|
||||||
- [ ] Domain is publicly accessible
|
|
||||||
- [ ] HTTPS is working (or HTTP for local testing)
|
|
||||||
- [ ] WebFinger works with your domain
|
|
||||||
- [ ] Actor is discoverable from other instances
|
|
||||||
- [ ] Posts federate to public instances
|
|
||||||
- [ ] Users can follow across instances
|
|
||||||
- [ ] Timelines show federated content
|
|
||||||
|
|
||||||
## Part 8: Monitoring During Tests
|
|
||||||
|
|
||||||
### Check Solar Network Logs
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Follow logs in real-time
|
|
||||||
dotnet run --project DysonNetwork.Sphere | grep -i activitypub
|
|
||||||
```
|
|
||||||
|
|
||||||
### Check Mastodon Logs
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker-compose -f docker-compose.mastodon-test.yml logs -f web | grep -i federation
|
|
||||||
```
|
|
||||||
|
|
||||||
### Monitor Database Activity
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Watch activity table
|
|
||||||
watch -n 2 'psql -d dyson_network -c "SELECT type, status, created_at FROM fediverse_activities ORDER BY created_at DESC LIMIT 10;"'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Check Network Traffic
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Monitor HTTP requests
|
|
||||||
tcpdump -i lo port 5000 or port 3001 -A
|
|
||||||
```
|
|
||||||
|
|
||||||
## Part 9: Advanced Testing
|
|
||||||
|
|
||||||
### Test HTTP Signature Fallbacks
|
|
||||||
|
|
||||||
Test with various signature headers:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# With Date header
|
|
||||||
curl -H "Date: $(date -u +%a,\ %d\ %b\ %Y\ %T\ GMT)" ...
|
|
||||||
|
|
||||||
# With Digest header
|
|
||||||
curl -H "Digest: SHA-256=$(echo -n '{}' | openssl dgst -sha256 -binary | base64)" ...
|
|
||||||
|
|
||||||
# Multiple signed headers
|
|
||||||
curl -H "Signature: keyId=\"...\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest\",signature=\"...\"" ...
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test Rate Limiting
|
|
||||||
|
|
||||||
Send multiple requests quickly:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
for i in {1..10}; do
|
|
||||||
curl -X POST http://solar.local:5000/activitypub/actors/solaruser/inbox \
|
|
||||||
-H "Content-Type: application/activity+json" \
|
|
||||||
-d '{"type":"Create",...}'
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test Large Posts
|
|
||||||
|
|
||||||
Send post with attachments:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST http://solar.local:5000/api/posts \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{
|
|
||||||
"content": "A post with an image",
|
|
||||||
"attachments": [{"id": "file-id"}],
|
|
||||||
"visibility": 0
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Part 10: Cleanup
|
|
||||||
|
|
||||||
### Stop Test Instances
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Stop Mastodon
|
|
||||||
docker-compose -f docker-compose.mastodon-test.yml down
|
|
||||||
|
|
||||||
# Stop GoToSocial
|
|
||||||
docker-compose -f docker-compose.gotosocial.yml down
|
|
||||||
|
|
||||||
# Remove data volumes
|
|
||||||
docker-compose -f docker-compose.mastodon-test.yml down -v
|
|
||||||
```
|
|
||||||
|
|
||||||
### Reset Solar Network Database
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Warning: This deletes all data!
|
|
||||||
cd DysonNetwork.Sphere
|
|
||||||
dotnet ef database drop
|
|
||||||
dotnet ef database update
|
|
||||||
```
|
|
||||||
|
|
||||||
### Remove /etc/hosts Entries
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo nano /etc/hosts
|
|
||||||
|
|
||||||
# Remove these lines:
|
|
||||||
# 127.0.0.1 mastodon.local
|
|
||||||
# 127.0.0.1 solar.local
|
|
||||||
# 127.0.0.1 gotosocial.local
|
|
||||||
```
|
|
||||||
|
|
||||||
## Next Steps After Testing
|
|
||||||
|
|
||||||
1. **Fix any issues found during testing**
|
|
||||||
2. **Add retry logic for failed deliveries**
|
|
||||||
3. **Implement activity queue for async processing**
|
|
||||||
4. **Add monitoring and metrics**
|
|
||||||
5. **Test with more instances (Pleroma, Pixelfed, etc.)**
|
|
||||||
6. **Add support for more activity types**
|
|
||||||
7. **Improve error handling and logging**
|
|
||||||
8. **Add admin interface for managing federation**
|
|
||||||
|
|
||||||
## Useful Tools
|
|
||||||
|
|
||||||
### ActivityPub Testing Tools
|
|
||||||
- [ActivityPub Playground](https://swicth.github.io/activity-pub-playground/)
|
|
||||||
- [FediTest](https://feditest.com/)
|
|
||||||
- [FediVerse.net](https://fedi.net/)
|
|
||||||
|
|
||||||
### HTTP Testing
|
|
||||||
- [curl](https://curl.se/)
|
|
||||||
- [httpie](https://httpie.io/)
|
|
||||||
- [Postman](https://www.postman.com/)
|
|
||||||
|
|
||||||
### JSON Inspection
|
|
||||||
- [jq](https://stedolan.github.io/jq/)
|
|
||||||
- [jsonpath.com](https://jsonpath.com/)
|
|
||||||
|
|
||||||
### Network Debugging
|
|
||||||
- [Wireshark](https://www.wireshark.org/)
|
|
||||||
- [tcpdump](https://www.tcpdump.org/)
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- [ActivityPub W3C Spec](https://www.w3.org/TR/activitypub/)
|
|
||||||
- [HTTP Signatures Draft](https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures)
|
|
||||||
- [WebFinger RFC 7033](https://tools.ietf.org/html/rfc7033)
|
|
||||||
- [Mastodon Federation Documentation](https://docs.joinmastodon.org/admin/federation/)
|
|
||||||
@@ -1,506 +0,0 @@
|
|||||||
# ActivityPub Testing Helper API
|
|
||||||
|
|
||||||
This document describes helper endpoints for testing ActivityPub federation.
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
|
|
||||||
These endpoints allow you to manually trigger ActivityPub activities for testing purposes without implementing the full UI federation integration yet.
|
|
||||||
|
|
||||||
## Helper Endpoints
|
|
||||||
|
|
||||||
### 1. Send Follow Activity
|
|
||||||
|
|
||||||
**Endpoint**: `POST /api/activitypub/test/follow`
|
|
||||||
|
|
||||||
**Description**: Sends a Follow activity to a remote actor
|
|
||||||
|
|
||||||
**Request Body**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"targetActorUri": "http://mastodon.local:3001/users/testuser"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"activityId": "http://solar.local:5000/activitypub/activities/...",
|
|
||||||
"targetActor": "http://mastodon.local:3001/users/testuser"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Send Like Activity
|
|
||||||
|
|
||||||
**Endpoint**: `POST /api/activitypub/test/like`
|
|
||||||
|
|
||||||
**Description**: Sends a Like activity for a post (can be local or remote)
|
|
||||||
|
|
||||||
**Request Body**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"postId": "POST_ID",
|
|
||||||
"targetActorUri": "http://mastodon.local:3001/users/testuser"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"activityId": "http://solar.local:5000/activitypub/activities/..."
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Send Announce (Boost) Activity
|
|
||||||
|
|
||||||
**Endpoint**: `POST /api/activitypub/test/announce`
|
|
||||||
|
|
||||||
**Description**: Boosts a post to followers
|
|
||||||
|
|
||||||
**Request Body**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"postId": "POST_ID"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Send Undo Activity
|
|
||||||
|
|
||||||
**Endpoint**: `POST /api/activitypub/test/undo`
|
|
||||||
|
|
||||||
**Description**: Undoes a previous activity
|
|
||||||
|
|
||||||
**Request Body**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"activityType": "Like", // or "Follow", "Announce"
|
|
||||||
"objectUri": "http://solar.local:5000/activitypub/objects/POST_ID"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. Get Federation Status
|
|
||||||
|
|
||||||
**Endpoint**: `GET /api/activitypub/test/status`
|
|
||||||
|
|
||||||
**Description**: Returns current federation statistics
|
|
||||||
|
|
||||||
**Response**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"actors": {
|
|
||||||
"total": 5,
|
|
||||||
"local": 1,
|
|
||||||
"remote": 4
|
|
||||||
},
|
|
||||||
"contents": {
|
|
||||||
"total": 25,
|
|
||||||
"byType": {
|
|
||||||
"Note": 20,
|
|
||||||
"Article": 5
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"relationships": {
|
|
||||||
"total": 8,
|
|
||||||
"accepted": 6,
|
|
||||||
"pending": 1,
|
|
||||||
"rejected": 1
|
|
||||||
},
|
|
||||||
"activities": {
|
|
||||||
"total": 45,
|
|
||||||
"byStatus": {
|
|
||||||
"Completed": 40,
|
|
||||||
"Pending": 3,
|
|
||||||
"Failed": 2
|
|
||||||
},
|
|
||||||
"byType": {
|
|
||||||
"Create": 20,
|
|
||||||
"Follow": 8,
|
|
||||||
"Accept": 6,
|
|
||||||
"Like": 5,
|
|
||||||
"Announce": 3,
|
|
||||||
"Undo": 2,
|
|
||||||
"Delete": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6. Get Recent Activities
|
|
||||||
|
|
||||||
**Endpoint**: `GET /api/activitypub/test/activities`
|
|
||||||
|
|
||||||
**Query Parameters**:
|
|
||||||
- `limit`: Number of activities to return (default: 20)
|
|
||||||
- `type`: Filter by activity type (optional)
|
|
||||||
|
|
||||||
**Response**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"activities": [
|
|
||||||
{
|
|
||||||
"id": "ACTIVITY_ID",
|
|
||||||
"type": "Follow",
|
|
||||||
"status": "Completed",
|
|
||||||
"actorUri": "http://mastodon.local:3001/users/testuser",
|
|
||||||
"objectUri": "http://solar.local:5000/activitypub/actors/solaruser",
|
|
||||||
"createdAt": "2024-01-15T10:30:00Z",
|
|
||||||
"errorMessage": null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 7. Get Actor Keys
|
|
||||||
|
|
||||||
**Endpoint**: `GET /api/activitypub/test/actors/{username}/keys`
|
|
||||||
|
|
||||||
**Description**: Returns the public/private key pair for a publisher
|
|
||||||
|
|
||||||
**Response**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"username": "solaruser",
|
|
||||||
"hasKeys": true,
|
|
||||||
"actorUri": "http://solar.local:5000/activitypub/actors/solaruser",
|
|
||||||
"publicKeyId": "http://solar.local:5000/activitypub/actors/solaruser#main-key",
|
|
||||||
"publicKey": "-----BEGIN PUBLIC KEY-----\n...",
|
|
||||||
"privateKeyStored": true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 8. Test HTTP Signature
|
|
||||||
|
|
||||||
**Endpoint**: `POST /api/activitypub/test/sign`
|
|
||||||
|
|
||||||
**Description**: Test if a signature string is valid for a given public key
|
|
||||||
|
|
||||||
**Request Body**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"publicKey": "-----BEGIN PUBLIC KEY-----\n...",
|
|
||||||
"signingString": "(request-target): post /inbox\nhost: example.com\ndate: ...",
|
|
||||||
"signature": "..."
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"valid": true,
|
|
||||||
"message": "Signature is valid"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Controller Implementation
|
|
||||||
|
|
||||||
Create `DysonNetwork.Sphere/ActivityPub/ActivityPubTestController.cs`:
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.ActivityPub;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/activitypub/test")]
|
|
||||||
[Authorize] // Require auth for testing
|
|
||||||
public class ActivityPubTestController(
|
|
||||||
AppDatabase db,
|
|
||||||
ActivityPubDeliveryService deliveryService,
|
|
||||||
ActivityPubKeyService keyService,
|
|
||||||
ActivityPubSignatureService signatureService,
|
|
||||||
IConfiguration configuration,
|
|
||||||
ILogger<ActivityPubTestController> logger
|
|
||||||
) : ControllerBase
|
|
||||||
{
|
|
||||||
[HttpPost("follow")]
|
|
||||||
public async Task<ActionResult> TestFollow([FromBody] TestFollowRequest request)
|
|
||||||
{
|
|
||||||
var currentUser = GetCurrentUser();
|
|
||||||
var publisher = await GetPublisherForUser(currentUser.Id);
|
|
||||||
|
|
||||||
if (publisher == null)
|
|
||||||
return BadRequest("Publisher not found");
|
|
||||||
|
|
||||||
var success = await deliveryService.SendFollowActivityAsync(
|
|
||||||
publisher.Id,
|
|
||||||
request.TargetActorUri
|
|
||||||
);
|
|
||||||
|
|
||||||
return Ok(new
|
|
||||||
{
|
|
||||||
success,
|
|
||||||
targetActorUri = request.TargetActorUri,
|
|
||||||
publisherId = publisher.Id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("like")]
|
|
||||||
public async Task<ActionResult> TestLike([FromBody] TestLikeRequest request)
|
|
||||||
{
|
|
||||||
var currentUser = GetCurrentUser();
|
|
||||||
var publisher = await GetPublisherForUser(currentUser.Id);
|
|
||||||
|
|
||||||
var success = await deliveryService.SendLikeActivityAsync(
|
|
||||||
request.PostId,
|
|
||||||
currentUser.Id,
|
|
||||||
request.TargetActorUri
|
|
||||||
);
|
|
||||||
|
|
||||||
return Ok(new { success, postId = request.PostId });
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("announce")]
|
|
||||||
public async Task<ActionResult> TestAnnounce([FromBody] TestAnnounceRequest request)
|
|
||||||
{
|
|
||||||
var post = await db.Posts.FindAsync(request.PostId);
|
|
||||||
if (post == null)
|
|
||||||
return NotFound();
|
|
||||||
|
|
||||||
var success = await deliveryService.SendCreateActivityAsync(post);
|
|
||||||
|
|
||||||
return Ok(new { success, postId = request.PostId });
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("undo")]
|
|
||||||
public async Task<ActionResult> TestUndo([FromBody] TestUndoRequest request)
|
|
||||||
{
|
|
||||||
var currentUser = GetCurrentUser();
|
|
||||||
var publisher = await GetPublisherForUser(currentUser.Id);
|
|
||||||
|
|
||||||
if (publisher == null)
|
|
||||||
return BadRequest("Publisher not found");
|
|
||||||
|
|
||||||
var success = await deliveryService.SendUndoActivityAsync(
|
|
||||||
request.ActivityType,
|
|
||||||
request.ObjectUri,
|
|
||||||
publisher.Id
|
|
||||||
);
|
|
||||||
|
|
||||||
return Ok(new { success, activityType = request.ActivityType });
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("status")]
|
|
||||||
public async Task<ActionResult> GetStatus()
|
|
||||||
{
|
|
||||||
var totalActors = await db.FediverseActors.CountAsync();
|
|
||||||
var localActors = await db.FediverseActors
|
|
||||||
.CountAsync(a => a.Uri.Contains("solar.local"));
|
|
||||||
|
|
||||||
var totalContents = await db.FediverseContents.CountAsync();
|
|
||||||
|
|
||||||
var relationships = await db.FediverseRelationships
|
|
||||||
.GroupBy(r => r.State)
|
|
||||||
.Select(g => new { State = g.Key, Count = g.Count() })
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
var activitiesByStatus = await db.FediverseActivities
|
|
||||||
.GroupBy(a => a.Status)
|
|
||||||
.Select(g => new { Status = g.Key, Count = g.Count() })
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
var activitiesByType = await db.FediverseActivities
|
|
||||||
.GroupBy(a => a.Type)
|
|
||||||
.Select(g => new { Type = g.Key, Count = g.Count() })
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
return Ok(new
|
|
||||||
{
|
|
||||||
actors = new
|
|
||||||
{
|
|
||||||
total = totalActors,
|
|
||||||
local = localActors,
|
|
||||||
remote = totalActors - localActors
|
|
||||||
},
|
|
||||||
contents = new
|
|
||||||
{
|
|
||||||
total = totalContents
|
|
||||||
},
|
|
||||||
relationships = relationships.ToDictionary(r => r.State.ToString(), r => r.Count),
|
|
||||||
activities = new
|
|
||||||
{
|
|
||||||
byStatus = activitiesByStatus.ToDictionary(a => a.Status.ToString(), a => a.Count),
|
|
||||||
byType = activitiesByType.ToDictionary(a => a.Type.ToString(), a => a.Count)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("activities")]
|
|
||||||
public async Task<ActionResult> GetActivities([FromQuery] int limit = 20, [FromQuery] string? type = null)
|
|
||||||
{
|
|
||||||
var query = db.FediverseActivities
|
|
||||||
.OrderByDescending(a => a.CreatedAt);
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(type))
|
|
||||||
{
|
|
||||||
query = query.Where(a => a.Type.ToString() == type);
|
|
||||||
}
|
|
||||||
|
|
||||||
var activities = await query
|
|
||||||
.Take(limit)
|
|
||||||
.Select(a => new
|
|
||||||
{
|
|
||||||
a.Id,
|
|
||||||
a.Type,
|
|
||||||
a.Status,
|
|
||||||
ActorUri = a.Actor.Uri,
|
|
||||||
ObjectUri = a.ObjectUri,
|
|
||||||
a.CreatedAt,
|
|
||||||
a.ErrorMessage
|
|
||||||
})
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
return Ok(new { activities });
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("actors/{username}/keys")]
|
|
||||||
public async Task<ActionResult> GetActorKeys(string username)
|
|
||||||
{
|
|
||||||
var publisher = await db.Publishers
|
|
||||||
.FirstOrDefaultAsync(p => p.Name == username);
|
|
||||||
|
|
||||||
if (publisher == null)
|
|
||||||
return NotFound();
|
|
||||||
|
|
||||||
var actorUrl = $"http://solar.local:5000/activitypub/actors/{username}";
|
|
||||||
|
|
||||||
var (privateKey, publicKey) = keyService.GenerateKeyPair();
|
|
||||||
|
|
||||||
return Ok(new
|
|
||||||
{
|
|
||||||
username,
|
|
||||||
hasKeys = publisher.Meta != null,
|
|
||||||
actorUri,
|
|
||||||
publicKeyId = $"{actorUrl}#main-key",
|
|
||||||
publicKey = publicKey,
|
|
||||||
privateKeyStored = publisher.Meta != null
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("sign")]
|
|
||||||
public ActionResult TestSignature([FromBody] TestSignatureRequest request)
|
|
||||||
{
|
|
||||||
var isValid = keyService.Verify(
|
|
||||||
request.PublicKey,
|
|
||||||
request.SigningString,
|
|
||||||
request.Signature
|
|
||||||
);
|
|
||||||
|
|
||||||
return Ok(new
|
|
||||||
{
|
|
||||||
valid = isValid,
|
|
||||||
message = isValid ? "Signature is valid" : "Signature is invalid"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<SnPublisher?> GetPublisherForUser(Guid accountId)
|
|
||||||
{
|
|
||||||
return await db.Publishers
|
|
||||||
.Include(p => p.Members)
|
|
||||||
.Where(p => p.Members.Any(m => m.AccountId == accountId))
|
|
||||||
.FirstOrDefaultAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Guid GetCurrentUser()
|
|
||||||
{
|
|
||||||
// Implement based on your auth system
|
|
||||||
return Guid.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TestFollowRequest
|
|
||||||
{
|
|
||||||
public string TargetActorUri { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TestLikeRequest
|
|
||||||
{
|
|
||||||
public Guid PostId { get; set; }
|
|
||||||
public string TargetActorUri { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TestAnnounceRequest
|
|
||||||
{
|
|
||||||
public Guid PostId { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TestUndoRequest
|
|
||||||
{
|
|
||||||
public string ActivityType { get; set; } = string.Empty;
|
|
||||||
public string ObjectUri { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TestSignatureRequest
|
|
||||||
{
|
|
||||||
public string PublicKey { get; set; } = string.Empty;
|
|
||||||
public string SigningString { get; set; } = string.Empty;
|
|
||||||
public string Signature { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing with Helper Endpoints
|
|
||||||
|
|
||||||
### 1. Test Follow
|
|
||||||
```bash
|
|
||||||
curl -X POST http://solar.local:5000/api/activitypub/test/follow \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-H "Authorization: Bearer YOUR_TOKEN" \
|
|
||||||
-d '{
|
|
||||||
"targetActorUri": "http://mastodon.local:3001/users/testuser"
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Test Like
|
|
||||||
```bash
|
|
||||||
curl -X POST http://solar.local:5000/api/activitypub/test/like \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-H "Authorization: Bearer YOUR_TOKEN" \
|
|
||||||
-d '{
|
|
||||||
"postId": "YOUR_POST_ID",
|
|
||||||
"targetActorUri": "http://mastodon.local:5000/activitypub/actors/mastodonuser"
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Check Status
|
|
||||||
```bash
|
|
||||||
curl http://solar.local:5000/api/activitypub/test/status \
|
|
||||||
-H "Authorization: Bearer YOUR_TOKEN"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Get Recent Activities
|
|
||||||
```bash
|
|
||||||
curl "http://solar.local:5000/api/activitypub/test/activities?limit=10" \
|
|
||||||
-H "Authorization: Bearer YOUR_TOKEN"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Integration with Main Flow
|
|
||||||
|
|
||||||
These helper endpoints can be used to:
|
|
||||||
|
|
||||||
1. **Quickly test federation** without full UI integration
|
|
||||||
2. **Debug specific activity types** in isolation
|
|
||||||
3. **Verify HTTP signatures** are correct
|
|
||||||
4. **Test error handling** for various scenarios
|
|
||||||
5. **Monitor federation status** during development
|
|
||||||
|
|
||||||
## Security Notes
|
|
||||||
|
|
||||||
- All test endpoints require authentication
|
|
||||||
- Use only in development/staging environments
|
|
||||||
- Remove or disable in production
|
|
||||||
- Rate limiting recommended if exposing to public
|
|
||||||
|
|
||||||
## Cleanup
|
|
||||||
|
|
||||||
After testing, you can:
|
|
||||||
|
|
||||||
1. Remove the test controller (optional)
|
|
||||||
2. Disable test endpoints
|
|
||||||
3. Clear test activities from database
|
|
||||||
4. Reset test relationships
|
|
||||||
|
|
||||||
```sql
|
|
||||||
-- Clear test data
|
|
||||||
DELETE FROM fediverse_activities WHERE created_at < NOW() - INTERVAL '1 day';
|
|
||||||
```
|
|
||||||
@@ -1,448 +0,0 @@
|
|||||||
# ActivityPub Testing - Complete Guide
|
|
||||||
|
|
||||||
This is the complete guide for testing ActivityPub federation in Solar Network.
|
|
||||||
|
|
||||||
## 📁 File Overview
|
|
||||||
|
|
||||||
| File | Purpose | When to Use |
|
|
||||||
|------|---------|--------------|
|
|
||||||
| `setup-activitypub-test.sh` | One-command setup of test environment | First time setup |
|
|
||||||
| `test-activitypub.sh` | Quick validation of basic functionality | After setup, before detailed tests |
|
|
||||||
| `ACTIVITYPUB_TESTING_QUICKSTART.md` | Quick start reference | Getting started quickly |
|
|
||||||
| `ACTIVITYPUB_TESTING_GUIDE.md` | Comprehensive testing scenarios | Full testing workflow |
|
|
||||||
| `ACTIVITYPUB_TESTING_QUICKREF.md` | Command and query reference | Daily testing |
|
|
||||||
| `ACTIVITYPUB_TESTING_HELPER_API.md` | Helper API for testing | Programmatic testing |
|
|
||||||
| `ACTIVITYPUB_TEST_RESULTS_TEMPLATE.md` | Track test results | During testing |
|
|
||||||
| `ACTIVITYPUB_IMPLEMENTATION.md` | Implementation details | Understanding the code |
|
|
||||||
| `ACTIVITYPUB_SUMMARY.md` | Feature summary | Reviewing what's implemented |
|
|
||||||
|
|
||||||
## 🚀 Quick Start (5 Minutes)
|
|
||||||
|
|
||||||
### 1. Setup Test Environment
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./setup-activitypub-test.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
This will:
|
|
||||||
- ✅ Configure `/etc/hosts`
|
|
||||||
- ✅ Start Mastodon via Docker
|
|
||||||
- ✅ Create test Mastodon account
|
|
||||||
- ✅ Apply database migrations
|
|
||||||
|
|
||||||
### 2. Validate Setup
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./test-activitypub.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
This checks:
|
|
||||||
- ✅ WebFinger endpoint
|
|
||||||
- ✅ Actor profile
|
|
||||||
- ✅ Public keys
|
|
||||||
- ✅ Database tables
|
|
||||||
|
|
||||||
### 3. Start Solar Network
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd DysonNetwork.Sphere
|
|
||||||
dotnet run
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Test Federation
|
|
||||||
|
|
||||||
1. Open http://mastodon.local:3001
|
|
||||||
2. Search for `@solaruser@solar.local`
|
|
||||||
3. Click Follow
|
|
||||||
4. Create a post in Solar Network
|
|
||||||
5. Verify it appears in Mastodon
|
|
||||||
|
|
||||||
## 📖 Recommended Reading Order
|
|
||||||
|
|
||||||
### For First-Time Testing
|
|
||||||
|
|
||||||
1. **Start Here**: `ACTIVITYPUB_TESTING_QUICKSTART.md`
|
|
||||||
- Overview of the setup
|
|
||||||
- Quick command reference
|
|
||||||
- Common commands
|
|
||||||
|
|
||||||
2. **Then**: `ACTIVITYPUB_TESTING_GUIDE.md`
|
|
||||||
- Detailed test scenarios
|
|
||||||
- Step-by-step instructions
|
|
||||||
- Troubleshooting
|
|
||||||
|
|
||||||
3. **Reference**: `ACTIVITYPUB_TESTING_QUICKREF.md`
|
|
||||||
- Command snippets
|
|
||||||
- Database queries
|
|
||||||
- Response codes
|
|
||||||
|
|
||||||
### During Testing
|
|
||||||
|
|
||||||
1. **Track Progress**: `ACTIVITYPUB_TEST_RESULTS_TEMPLATE.md`
|
|
||||||
- Checklists for each test
|
|
||||||
- Results tracking
|
|
||||||
- Issue logging
|
|
||||||
|
|
||||||
2. **Helper API**: `ACTIVITYPUB_TESTING_HELPER_API.md`
|
|
||||||
- Manual testing endpoints
|
|
||||||
- Debugging tools
|
|
||||||
- Status monitoring
|
|
||||||
|
|
||||||
### For Understanding
|
|
||||||
|
|
||||||
1. **Implementation**: `ACTIVITYPUB_IMPLEMENTATION.md`
|
|
||||||
- Architecture details
|
|
||||||
- Service descriptions
|
|
||||||
- Data flow diagrams
|
|
||||||
|
|
||||||
2. **Features**: `ACTIVITYPUB_SUMMARY.md`
|
|
||||||
- What's implemented
|
|
||||||
- Model relationships
|
|
||||||
- API endpoints
|
|
||||||
|
|
||||||
## 🔍 Test Scenarios Summary
|
|
||||||
|
|
||||||
### Basic Functionality (All instances must pass)
|
|
||||||
|
|
||||||
- [ ] WebFinger discovery works
|
|
||||||
- [ ] Actor profile is valid JSON-LD
|
|
||||||
- [ ] Public key is present
|
|
||||||
- [ ] Outbox returns public posts
|
|
||||||
- [ ] Inbox accepts activities
|
|
||||||
|
|
||||||
### Federation - Follow
|
|
||||||
|
|
||||||
- [ ] Remote user can follow local user
|
|
||||||
- [ ] Local user can follow remote user
|
|
||||||
- [ ] Accept activity is sent/received
|
|
||||||
- [ ] Relationship state is correct
|
|
||||||
- [ ] Unfollow works correctly
|
|
||||||
|
|
||||||
### Federation - Content
|
|
||||||
|
|
||||||
- [ ] Local posts federate to remote instances
|
|
||||||
- [ ] Remote posts appear in local database
|
|
||||||
- [ ] Post content is preserved
|
|
||||||
- [ ] Timestamps are correct
|
|
||||||
- [ ] Attachments are handled
|
|
||||||
- [ ] Content warnings are respected
|
|
||||||
|
|
||||||
### Federation - Interactions
|
|
||||||
|
|
||||||
- [ ] Likes federate correctly
|
|
||||||
- [ ] Likes appear in both instances
|
|
||||||
- [ ] Replies federate correctly
|
|
||||||
- [ ] Reply threading works
|
|
||||||
- [ ] Boosts/Announces work
|
|
||||||
- [ ] Undo activities work
|
|
||||||
|
|
||||||
### Security
|
|
||||||
|
|
||||||
- [ ] HTTP signatures are verified
|
|
||||||
- [ ] Invalid signatures are rejected
|
|
||||||
- [ ] Keys are properly stored
|
|
||||||
- [ ] Private keys never exposed
|
|
||||||
|
|
||||||
## 🐛 Common Issues & Solutions
|
|
||||||
|
|
||||||
### Issue: "Failed to verify signature"
|
|
||||||
|
|
||||||
**Causes**:
|
|
||||||
1. Signature header format is wrong
|
|
||||||
2. Public key doesn't match keyId
|
|
||||||
3. Date header is too old (>5 minutes)
|
|
||||||
4. Request body doesn't match digest
|
|
||||||
|
|
||||||
**Solutions**:
|
|
||||||
1. Check signature format: `keyId="...",algorithm="...",headers="...",signature="..."`
|
|
||||||
2. Verify keyId in actor profile
|
|
||||||
3. Ensure Date header is recent
|
|
||||||
4. Check body is exactly what was signed
|
|
||||||
|
|
||||||
### Issue: "Target actor or inbox not found"
|
|
||||||
|
|
||||||
**Causes**:
|
|
||||||
1. Actor hasn't been fetched yet
|
|
||||||
2. Actor URL is incorrect
|
|
||||||
3. Remote instance is inaccessible
|
|
||||||
|
|
||||||
**Solutions**:
|
|
||||||
1. Manually fetch actor first
|
|
||||||
2. Verify actor URL is correct
|
|
||||||
3. Test accessibility with curl
|
|
||||||
|
|
||||||
### Issue: Activities not arriving
|
|
||||||
|
|
||||||
**Causes**:
|
|
||||||
1. Network connectivity issue
|
|
||||||
2. Remote instance is down
|
|
||||||
3. Activity wasn't queued properly
|
|
||||||
|
|
||||||
**Solutions**:
|
|
||||||
1. Check network connectivity
|
|
||||||
2. Verify remote instance is running
|
|
||||||
3. Check fediverse_activities table for status
|
|
||||||
|
|
||||||
## 📊 Monitoring During Tests
|
|
||||||
|
|
||||||
### Check Logs
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Solar Network ActivityPub logs
|
|
||||||
dotnet run --project DysonNetwork.Sphere 2>&1 | grep -i activitypub
|
|
||||||
|
|
||||||
# Mastodon federation logs
|
|
||||||
docker compose -f docker-compose.mastodon-test.yml logs -f web | grep -i federation
|
|
||||||
```
|
|
||||||
|
|
||||||
### Monitor Database
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Watch activity table
|
|
||||||
watch -n 2 'psql -d dyson_network -c \
|
|
||||||
"SELECT type, status, created_at FROM fediverse_activities ORDER BY created_at DESC LIMIT 5;"'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test Network
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Test connectivity between instances
|
|
||||||
curl -v http://mastodon.local:3001
|
|
||||||
curl -v http://solar.local:5000
|
|
||||||
|
|
||||||
# Test with traceroute (if available)
|
|
||||||
traceroute mastodon.local
|
|
||||||
traceroute solar.local
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🎯 Success Criteria
|
|
||||||
|
|
||||||
### Minimal Viable Federation
|
|
||||||
|
|
||||||
To consider ActivityPub implementation "working", all of these must pass:
|
|
||||||
|
|
||||||
- ✅ WebFinger returns actor links
|
|
||||||
- ✅ Actor profile has all required fields
|
|
||||||
- ✅ Follow relationships work bidirectionally
|
|
||||||
- ✅ Public posts federate to followers
|
|
||||||
- ✅ Incoming posts are stored correctly
|
|
||||||
- ✅ HTTP signatures are verified
|
|
||||||
- ✅ Basic interaction types work (Like, Reply)
|
|
||||||
|
|
||||||
### Full Production Ready
|
|
||||||
|
|
||||||
For production, also need:
|
|
||||||
|
|
||||||
- ✅ Activity queue with retry logic
|
|
||||||
- ✅ Rate limiting on outgoing deliveries
|
|
||||||
- ✅ Monitoring and alerting
|
|
||||||
- ✅ Admin interface for federation management
|
|
||||||
- ✅ Content filtering and moderation
|
|
||||||
- ✅ Instance blocking capabilities
|
|
||||||
- ✅ Performance optimization for high volume
|
|
||||||
|
|
||||||
## 🔐 Security Checklist
|
|
||||||
|
|
||||||
During testing, verify:
|
|
||||||
|
|
||||||
- [ ] Private keys are never logged
|
|
||||||
- [ ] Private keys are never returned in API responses
|
|
||||||
- [ ] Only public keys are in actor profiles
|
|
||||||
- [ ] All incoming activities are signature-verified
|
|
||||||
- [ ] Invalid signatures are rejected with 401
|
|
||||||
- [ ] TLS is used in production
|
|
||||||
- [ ] Host header is verified against request URL
|
|
||||||
|
|
||||||
## 📈 Performance Metrics
|
|
||||||
|
|
||||||
Track these during testing:
|
|
||||||
|
|
||||||
| Metric | Target | Actual |
|
|
||||||
|--------|--------|--------|
|
|
||||||
| WebFinger response time | <500ms | ___ ms |
|
|
||||||
| Actor fetch time | <1s | ___ ms |
|
|
||||||
| Signature verification time | <100ms | ___ ms |
|
|
||||||
| Activity processing time | <500ms | ___ ms |
|
|
||||||
| Outgoing delivery success rate | >95% | ___% |
|
|
||||||
| Outgoing delivery time | <5s | ___ ms |
|
|
||||||
|
|
||||||
## 🧪 Testing Checklist
|
|
||||||
|
|
||||||
### Self-Hosted Instance Tests
|
|
||||||
|
|
||||||
**Setup**:
|
|
||||||
- [ ] Setup script completed
|
|
||||||
- [ ] Mast containers running
|
|
||||||
- [ ] Solar Network running
|
|
||||||
- [ ] /etc/hosts configured
|
|
||||||
- [ ] Database migrations applied
|
|
||||||
|
|
||||||
**Basic Federation**:
|
|
||||||
- [ ] WebFinger works
|
|
||||||
- [ ] Actor profile valid
|
|
||||||
- [ ] Public key present
|
|
||||||
- [ ] Outbox accessible
|
|
||||||
|
|
||||||
**Follow Flow**:
|
|
||||||
- [ ] Mastodon → Solar follow works
|
|
||||||
- [ ] Solar → Mastodon follow works
|
|
||||||
- [ ] Accept activity sent
|
|
||||||
- [ ] Relationship state correct
|
|
||||||
|
|
||||||
**Content Flow**:
|
|
||||||
- [ ] Solar posts appear in Mastodon
|
|
||||||
- [ ] Mastodon posts appear in Solar
|
|
||||||
- [ ] Content preserved correctly
|
|
||||||
- [ ] Timestamps correct
|
|
||||||
|
|
||||||
**Interactions**:
|
|
||||||
- [ ] Likes work both ways
|
|
||||||
- [ ] Replies work both ways
|
|
||||||
- [ ] Boosts work both ways
|
|
||||||
- [ ] Undo works
|
|
||||||
|
|
||||||
**Security**:
|
|
||||||
- [ ] HTTP signatures verified
|
|
||||||
- [ ] Invalid signatures rejected
|
|
||||||
- [ ] Keys properly managed
|
|
||||||
|
|
||||||
### Real Instance Tests
|
|
||||||
|
|
||||||
**Discovery**:
|
|
||||||
- [ ] Domain publicly accessible
|
|
||||||
- [ ] WebFinger works from public internet
|
|
||||||
- [ ] Actor discoverable from public instances
|
|
||||||
|
|
||||||
**Federation**:
|
|
||||||
- [ ] Posts federate to public instances
|
|
||||||
- [ ] Follows work with public instances
|
|
||||||
- [ ] Interactions work with public instances
|
|
||||||
|
|
||||||
## 📝 Testing Notes
|
|
||||||
|
|
||||||
### What Worked Well
|
|
||||||
1. _____________________
|
|
||||||
2. _____________________
|
|
||||||
3. _____________________
|
|
||||||
|
|
||||||
### What Needs Improvement
|
|
||||||
1. _____________________
|
|
||||||
2. _____________________
|
|
||||||
3. _____________________
|
|
||||||
|
|
||||||
### Bugs Found
|
|
||||||
| # | Description | Severity | Status |
|
|
||||||
|---|-------------|----------|--------|
|
|
||||||
| 1 | _____________________ | Low/Medium/High | ☐ Open/☐ Fixed |
|
|
||||||
| 2 | _____________________ | Low/Medium/High | ☐ Open/☐ Fixed |
|
|
||||||
| 3 | _____________________ | Low/Medium/High | ☐ Open/☐ Fixed |
|
|
||||||
|
|
||||||
## 🎓 Learning Resources
|
|
||||||
|
|
||||||
### ActivityPub Specification
|
|
||||||
- [W3C ActivityPub Recommendation](https://www.w3.org/TR/activitypub/)
|
|
||||||
- [ActivityStreams 2.0](https://www.w3.org/TR/activitystreams-core/)
|
|
||||||
- [HTTP Signatures Draft](https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures)
|
|
||||||
|
|
||||||
### Implementation Guides
|
|
||||||
- [Mastodon Federation Guide](https://docs.joinmastodon.org/admin/federation/)
|
|
||||||
- [ActivityPub Testing Best Practices](https://blog.joinmastodon.org/2018/06/27/how-to-implement-a-basic-activitypub-server/)
|
|
||||||
- [Federation Testing Checklist](https://docs.joinmastodon.org/spec/activitypub/)
|
|
||||||
|
|
||||||
### Tools
|
|
||||||
- [ActivityPub Playground](https://swicth.github.io/activity-pub-playground/)
|
|
||||||
- [FediTest](https://feditest.com/)
|
|
||||||
- [JSONPath Online Evaluator](https://jsonpath.com/)
|
|
||||||
|
|
||||||
## 🔄 Next Steps After Testing
|
|
||||||
|
|
||||||
### Phase 1: Fix Issues
|
|
||||||
- Address all bugs found during testing
|
|
||||||
- Improve error messages
|
|
||||||
- Add better logging
|
|
||||||
|
|
||||||
### Phase 2: Enhance Features
|
|
||||||
- Implement activity queue
|
|
||||||
- Add retry logic
|
|
||||||
- Add rate limiting
|
|
||||||
- Implement instance blocking
|
|
||||||
|
|
||||||
### Phase 3: Production Readiness
|
|
||||||
- Add monitoring and metrics
|
|
||||||
- Add admin interface
|
|
||||||
- Add content filtering
|
|
||||||
- Implement moderation tools
|
|
||||||
|
|
||||||
### Phase 4: Additional Features
|
|
||||||
- Support more activity types
|
|
||||||
- Support media attachments
|
|
||||||
- Support polls
|
|
||||||
- Support custom emojis
|
|
||||||
|
|
||||||
## 📞 Getting Help
|
|
||||||
|
|
||||||
If you encounter issues:
|
|
||||||
|
|
||||||
1. **Check logs**: See the logs section above
|
|
||||||
2. **Review troubleshooting**: See `ACTIVITYPUB_TESTING_GUIDE.md` Part 5
|
|
||||||
3. **Check database queries**: Use queries from `ACTIVITYPUB_TESTING_QUICKREF.md`
|
|
||||||
4. **Validate signatures**: Use helper API in `ACTIVITYPUB_TESTING_HELPER_API.md`
|
|
||||||
|
|
||||||
## ✨ Quick Test Commands
|
|
||||||
|
|
||||||
### All-in-One Test Sequence
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. Setup
|
|
||||||
./setup-activitypub-test.sh
|
|
||||||
|
|
||||||
# 2. Validate
|
|
||||||
./test-activitypub.sh
|
|
||||||
|
|
||||||
# 3. Test WebFinger
|
|
||||||
curl "http://solar.local:5000/.well-known/webfinger?resource=acct:solaruser@solar.local"
|
|
||||||
|
|
||||||
# 4. Test Actor
|
|
||||||
curl -H "Accept: application/activity+json" \
|
|
||||||
http://solar.local:5000/activitypub/actors/solaruser
|
|
||||||
|
|
||||||
# 5. Test Follow (from Mastodon UI)
|
|
||||||
# Open http://mastodon.local:3001 and follow @solaruser@solar.local
|
|
||||||
|
|
||||||
# 6. Check database
|
|
||||||
psql -d dyson_network -c "SELECT * FROM fediverse_relationships;"
|
|
||||||
|
|
||||||
# 7. Test Post (create in Solar Network UI)
|
|
||||||
# Should appear in http://mastodon.local:3001
|
|
||||||
|
|
||||||
# 8. Verify content
|
|
||||||
psql -d dyson_network -c "SELECT * FROM fediverse_contents;"
|
|
||||||
|
|
||||||
# 9. Test Like (like from Mastodon UI)
|
|
||||||
# Should appear in fediverse_reactions table
|
|
||||||
|
|
||||||
# 10. Check activities
|
|
||||||
psql -d dyson_network -c "SELECT type, status FROM fediverse_activities;"
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🎉 Conclusion
|
|
||||||
|
|
||||||
You now have everything needed to test ActivityPub federation for Solar Network:
|
|
||||||
|
|
||||||
- ✅ Self-hosted test environment (Mastodon)
|
|
||||||
- ✅ Setup automation (setup script)
|
|
||||||
- ✅ Quick validation (test script)
|
|
||||||
- ✅ Comprehensive testing guide
|
|
||||||
- ✅ Helper API for programmatic testing
|
|
||||||
- ✅ Quick reference for daily use
|
|
||||||
- ✅ Results template for tracking progress
|
|
||||||
|
|
||||||
**Recommended workflow**:
|
|
||||||
1. Run `setup-activitypub-test.sh`
|
|
||||||
2. Run `test-activitypub.sh` for validation
|
|
||||||
3. Follow scenarios in `ACTIVITYPUB_TESTING_GUIDE.md`
|
|
||||||
4. Use `ACTIVITYPUB_TESTING_HELPER_API.md` for specific tests
|
|
||||||
5. Track results in `ACTIVITYPUB_TEST_RESULTS_TEMPLATE.md`
|
|
||||||
6. Reference `ACTIVITYPUB_TESTING_QUICKREF.md` for commands
|
|
||||||
|
|
||||||
Good luck with your federation testing! 🚀
|
|
||||||
@@ -1,356 +0,0 @@
|
|||||||
# ActivityPub Testing Quick Reference
|
|
||||||
|
|
||||||
## Quick Test Commands
|
|
||||||
|
|
||||||
### 1. Test WebFinger
|
|
||||||
```bash
|
|
||||||
curl "http://solar.local:5000/.well-known/webfinger?resource=acct:username@solar.local"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Fetch Actor Profile
|
|
||||||
```bash
|
|
||||||
curl -H "Accept: application/activity+json" \
|
|
||||||
http://solar.local:5000/activitypub/actors/username
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Get Outbox
|
|
||||||
```bash
|
|
||||||
curl -H "Accept: application/activity+json" \
|
|
||||||
http://solar.local:5000/activitypub/actors/username/outbox
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Send Test Follow (from remote)
|
|
||||||
```bash
|
|
||||||
curl -X POST http://solar.local:5000/activitypub/actors/username/inbox \
|
|
||||||
-H "Content-Type: application/activity+json" \
|
|
||||||
-d '{
|
|
||||||
"@context": "https://www.w3.org/ns/activitystreams",
|
|
||||||
"id": "https://mastodon.local:3001/follow-123",
|
|
||||||
"type": "Follow",
|
|
||||||
"actor": "https://mastodon.local:3001/users/remoteuser",
|
|
||||||
"object": "https://solar.local:5000/activitypub/actors/username"
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. Send Test Create (post)
|
|
||||||
```bash
|
|
||||||
curl -X POST http://solar.local:5000/activitypub/actors/username/inbox \
|
|
||||||
-H "Content-Type: application/activity+json" \
|
|
||||||
-d '{
|
|
||||||
"@context": "https://www.w3.org/ns/activitystreams",
|
|
||||||
"id": "https://mastodon.local:3001/post-123",
|
|
||||||
"type": "Create",
|
|
||||||
"actor": "https://mastodon.local:3001/users/remoteuser",
|
|
||||||
"object": {
|
|
||||||
"id": "https://mastodon.local:3001/objects/post-123",
|
|
||||||
"type": "Note",
|
|
||||||
"content": "Hello from Mastodon! @username@solar.local",
|
|
||||||
"attributedTo": "https://mastodon.local:3001/users/remoteuser",
|
|
||||||
"to": ["https://www.w3.org/ns/activitystreams#Public"]
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6. Send Test Like
|
|
||||||
```bash
|
|
||||||
curl -X POST http://solar.local:5000/activitypub/actors/username/inbox \
|
|
||||||
-H "Content-Type: application/activity+json" \
|
|
||||||
-d '{
|
|
||||||
"@context": "https://www.w3.org/ns/activitystreams",
|
|
||||||
"id": "https://mastodon.local:3001/like-123",
|
|
||||||
"type": "Like",
|
|
||||||
"actor": "https://mastodon.local:3001/users/remoteuser",
|
|
||||||
"object": "https://solar.local:5000/activitypub/objects/post-id"
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Database Queries
|
|
||||||
|
|
||||||
### Check Actors
|
|
||||||
```sql
|
|
||||||
SELECT id, uri, username, display_name, instance_id
|
|
||||||
FROM fediverse_actors;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Check Contents
|
|
||||||
```sql
|
|
||||||
SELECT id, uri, type, content, actor_id, created_at
|
|
||||||
FROM fediverse_contents
|
|
||||||
ORDER BY created_at DESC
|
|
||||||
LIMIT 20;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Check Relationships
|
|
||||||
```sql
|
|
||||||
SELECT r.id, a1.uri as actor, a2.uri as target, r.state, r.is_following
|
|
||||||
FROM fediverse_relationships r
|
|
||||||
JOIN fediverse_actors a1 ON r.actor_id = a1.id
|
|
||||||
JOIN fediverse_actors a2 ON r.target_actor_id = a2.id;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Check Activities
|
|
||||||
```sql
|
|
||||||
SELECT type, status, error_message, created_at
|
|
||||||
FROM fediverse_activities
|
|
||||||
ORDER BY created_at DESC
|
|
||||||
LIMIT 20;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Check Reactions
|
|
||||||
```sql
|
|
||||||
SELECT r.type, c.uri as content_uri, a.uri as actor_uri
|
|
||||||
FROM fediverse_reactions r
|
|
||||||
JOIN fediverse_contents c ON r.content_id = c.id
|
|
||||||
JOIN fediverse_actors a ON r.actor_id = a.id;
|
|
||||||
```
|
|
||||||
|
|
||||||
## Check Keys in Publisher
|
|
||||||
```sql
|
|
||||||
SELECT id, name, meta
|
|
||||||
FROM publishers
|
|
||||||
WHERE meta IS NOT NULL;
|
|
||||||
```
|
|
||||||
|
|
||||||
## Docker Commands
|
|
||||||
|
|
||||||
### Start Mastodon
|
|
||||||
```bash
|
|
||||||
docker-compose -f docker-compose.mastodon-test.yml up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
### View Mastodon Logs
|
|
||||||
```bash
|
|
||||||
docker-compose -f docker-compose.mastodon-test.yml logs -f web
|
|
||||||
```
|
|
||||||
|
|
||||||
### Stop Mastodon
|
|
||||||
```bash
|
|
||||||
docker-compose -f docker-compose.mastodon-test.yml down
|
|
||||||
```
|
|
||||||
|
|
||||||
### Start GoToSocial
|
|
||||||
```bash
|
|
||||||
docker-compose -f docker-compose.gotosocial.yml up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
## Solar Network Commands
|
|
||||||
|
|
||||||
### Run Migrations
|
|
||||||
```bash
|
|
||||||
cd DysonNetwork.Sphere
|
|
||||||
dotnet ef database update
|
|
||||||
```
|
|
||||||
|
|
||||||
### Run with Debug Logging
|
|
||||||
```bash
|
|
||||||
dotnet run --project DysonNetwork.Sphere -- --logging:LogLevel:DysonNetwork.Sphere.ActivityPub=Trace
|
|
||||||
```
|
|
||||||
|
|
||||||
## Common Response Codes
|
|
||||||
|
|
||||||
| Code | Meaning |
|
|
||||||
|------|---------|
|
|
||||||
| 200 | Success |
|
|
||||||
| 202 | Accepted (activity queued) |
|
|
||||||
| 401 | Unauthorized (invalid signature) |
|
|
||||||
| 404 | Not found (user/post doesn't exist) |
|
|
||||||
| 400 | Bad request (invalid activity) |
|
|
||||||
|
|
||||||
## Activity Status Codes
|
|
||||||
|
|
||||||
| Status | Code | Meaning |
|
|
||||||
|--------|------|---------|
|
|
||||||
| Pending | 0 | Activity waiting to be processed |
|
|
||||||
| Processing | 1 | Activity being processed |
|
|
||||||
| Completed | 2 | Activity successfully processed |
|
|
||||||
| Failed | 3 | Activity processing failed |
|
|
||||||
|
|
||||||
## Relationship States
|
|
||||||
|
|
||||||
| State | Code | Meaning |
|
|
||||||
|--------|------|---------|
|
|
||||||
| Pending | 0 | Follow request sent, waiting for Accept |
|
|
||||||
| Accepted | 1 | Follow accepted, relationship active |
|
|
||||||
| Rejected | 2 | Follow rejected |
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### "Failed to verify signature"
|
|
||||||
|
|
||||||
**Check**: Signature header format
|
|
||||||
```bash
|
|
||||||
# Should be:
|
|
||||||
Signature: keyId="...",algorithm="rsa-sha256",headers="...",signature="..."
|
|
||||||
```
|
|
||||||
|
|
||||||
**Check**: Public key in actor profile
|
|
||||||
```bash
|
|
||||||
curl -H "Accept: application/activity+json" \
|
|
||||||
http://solar.local:5000/activitypub/actors/username | jq '.publicKey'
|
|
||||||
```
|
|
||||||
|
|
||||||
### "Actor not found"
|
|
||||||
|
|
||||||
**Check**: Actor exists in database
|
|
||||||
```bash
|
|
||||||
psql -d dyson_network -c \
|
|
||||||
"SELECT * FROM fediverse_actors WHERE uri = '...';"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Check**: Actor URL is accessible
|
|
||||||
```bash
|
|
||||||
curl -v http://remote-instance.com/users/username
|
|
||||||
```
|
|
||||||
|
|
||||||
### "Content already exists"
|
|
||||||
|
|
||||||
This is normal behavior - the system is deduplicating.
|
|
||||||
|
|
||||||
### "Target publisher not found"
|
|
||||||
|
|
||||||
**Check**: Publisher exists
|
|
||||||
```bash
|
|
||||||
psql -d dyson_network -c \
|
|
||||||
"SELECT * FROM publishers WHERE name = '...';"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Test Sequence
|
|
||||||
|
|
||||||
### Full Federation Test
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. Start both instances
|
|
||||||
docker-compose -f docker-compose.mastodon-test.yml up -d
|
|
||||||
dotnet run --project DysonNetwork.Sphere
|
|
||||||
|
|
||||||
# 2. Test WebFinger
|
|
||||||
curl "http://solar.local:5000/.well-known/webfinger?resource=acct:solaruser@solar.local"
|
|
||||||
|
|
||||||
# 3. Get Actor
|
|
||||||
curl -H "Accept: application/activity+json" \
|
|
||||||
http://solar.local:5000/activitypub/actors/solaruser
|
|
||||||
|
|
||||||
# 4. Send Follow from Mastodon
|
|
||||||
# (Do this in Mastodon UI or use curl above)
|
|
||||||
|
|
||||||
# 5. Check database
|
|
||||||
psql -d dyson_network -c "SELECT * FROM fediverse_relationships;"
|
|
||||||
|
|
||||||
# 6. Send Create (post) from Mastodon
|
|
||||||
# (Use curl command above)
|
|
||||||
|
|
||||||
# 7. Check content
|
|
||||||
psql -d dyson_network -c "SELECT * FROM fediverse_contents;"
|
|
||||||
|
|
||||||
# 8. Send Like from Mastodon
|
|
||||||
# (Use curl command above)
|
|
||||||
|
|
||||||
# 9. Check reactions
|
|
||||||
psql -d dyson_network -c "SELECT * FROM fediverse_reactions;"
|
|
||||||
|
|
||||||
# 10. Check activities
|
|
||||||
psql -d dyson_network -c "SELECT type, status FROM fediverse_activities;"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Test URLs
|
|
||||||
|
|
||||||
| Instance | Web | API | ActivityPub |
|
|
||||||
|----------|-----|-----|-----------|
|
|
||||||
| Solar Network | http://solar.local:5000 | http://solar.local:5000/api | http://solar.local:5000/activitypub |
|
|
||||||
| Mastodon | http://mastodon.local:3001 | http://mastodon.local:3001/api/v1 | http://mastodon.local:3001/inbox |
|
|
||||||
|
|
||||||
## Environment Variables
|
|
||||||
|
|
||||||
### Solar Network
|
|
||||||
```bash
|
|
||||||
export SOLAR_DOMAIN="solar.local"
|
|
||||||
export SOLAR_URL="http://solar.local:5000"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Mastodon
|
|
||||||
```bash
|
|
||||||
export MASTODON_DOMAIN="mastodon.local"
|
|
||||||
export MASTODON_URL="http://mastodon.local:3001"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Useful jq Commands
|
|
||||||
|
|
||||||
### Extract Actor ID
|
|
||||||
```bash
|
|
||||||
curl ... | jq '.id'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Extract Inbox URL
|
|
||||||
```bash
|
|
||||||
curl ... | jq '.inbox'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Extract Public Key
|
|
||||||
```bash
|
|
||||||
curl ... | jq '.publicKey.publicKeyPem'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Pretty Print Activity
|
|
||||||
```bash
|
|
||||||
curl ... | jq '.'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Extract Activity Type
|
|
||||||
```bash
|
|
||||||
curl ... | jq '.type'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Network Setup
|
|
||||||
|
|
||||||
### /etc/hosts
|
|
||||||
```
|
|
||||||
127.0.0.1 solar.local
|
|
||||||
127.0.0.1 mastodon.local
|
|
||||||
127.0.0.1 gotosocial.local
|
|
||||||
```
|
|
||||||
|
|
||||||
### Ports Used
|
|
||||||
- Solar Network: 5000
|
|
||||||
- Mastodon: 3001 (web), 4000 (streaming)
|
|
||||||
- GoToSocial: 3002
|
|
||||||
- PostgreSQL: 5432
|
|
||||||
- Redis: 6379
|
|
||||||
- Elasticsearch: 9200
|
|
||||||
|
|
||||||
## File Locations
|
|
||||||
|
|
||||||
### Docker Compose Files
|
|
||||||
- `docker-compose.mastodon-test.yml`
|
|
||||||
- `docker-compose.gotosocial.yml`
|
|
||||||
|
|
||||||
### Environment Files
|
|
||||||
- `.env.mastodon`
|
|
||||||
|
|
||||||
### Data Volumes
|
|
||||||
- `./mastodon-data/`
|
|
||||||
- `./gotosocial-data/`
|
|
||||||
|
|
||||||
## Clean Up Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Reset database
|
|
||||||
psql -d dyson_network <<EOF
|
|
||||||
TRUNCATE fediverse_activities CASCADE;
|
|
||||||
TRUNCATE fediverse_relationships CASCADE;
|
|
||||||
TRUNCATE fediverse_reactions CASCADE;
|
|
||||||
TRUNCATE fediverse_contents CASCADE;
|
|
||||||
TRUNCATE fediverse_actors CASCADE;
|
|
||||||
TRUNCATE fediverse_instances CASCADE;
|
|
||||||
UPDATE publishers SET meta = NULL WHERE meta IS NOT NULL;
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Reset everything
|
|
||||||
docker-compose -f docker-compose.mastodon-test.yml down -v
|
|
||||||
docker-compose -f docker-compose.gotosocial.yml down -v
|
|
||||||
psql -d dyson_network <<EOF
|
|
||||||
DROP SCHEMA public CASCADE;
|
|
||||||
CREATE SCHEMA public;
|
|
||||||
EOF
|
|
||||||
dotnet ef database drop
|
|
||||||
dotnet ef database update
|
|
||||||
```
|
|
||||||
@@ -1,289 +0,0 @@
|
|||||||
# ActivityPub Testing - Quick Start
|
|
||||||
|
|
||||||
This directory contains everything you need to test ActivityPub federation for Solar Network.
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
### 1. Run the Setup Script
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./setup-activitypub-test.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
This will:
|
|
||||||
- ✅ Check prerequisites (Docker, PostgreSQL)
|
|
||||||
- ✅ Update `/etc/hosts` with test domains
|
|
||||||
- ✅ Generate Mastodon environment file
|
|
||||||
- ✅ Create Docker Compose file
|
|
||||||
- ✅ Start Mastodon containers
|
|
||||||
- ✅ Create test Mastodon account
|
|
||||||
- ✅ Apply Solar Network migrations
|
|
||||||
|
|
||||||
### 2. Start Solar Network
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd DysonNetwork.Sphere
|
|
||||||
dotnet run
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Test Federation
|
|
||||||
|
|
||||||
Follow the scenarios in [ACTIVITYPUB_TESTING_GUIDE.md](ACTIVITYPUB_TESTING_GUIDE.md)
|
|
||||||
|
|
||||||
## Test Instances
|
|
||||||
|
|
||||||
| Service | URL | Notes |
|
|
||||||
|---------|-----|-------|
|
|
||||||
| Solar Network | http://solar.local:5000 | Your implementation |
|
|
||||||
| Mastodon | http://mastodon.local:3001 | Test instance |
|
|
||||||
| Mastodon Streaming | http://mastodon.local:4000 | WebSocket |
|
|
||||||
|
|
||||||
## Test Accounts
|
|
||||||
|
|
||||||
### Solar Network
|
|
||||||
- Create via UI or API
|
|
||||||
- Username: `solaruser` (or your choice)
|
|
||||||
|
|
||||||
### Mastodon
|
|
||||||
- Username: `testuser@mastodon.local`
|
|
||||||
- Password: `TestPassword123!`
|
|
||||||
- Role: Admin
|
|
||||||
|
|
||||||
## Quick Test Commands
|
|
||||||
|
|
||||||
### Test WebFinger
|
|
||||||
```bash
|
|
||||||
curl "http://solar.local:5000/.well-known/webfinger?resource=acct:solaruser@solar.local"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test Actor
|
|
||||||
```bash
|
|
||||||
curl -H "Accept: application/activity+json" \
|
|
||||||
http://solar.local:5000/activitypub/actors/solaruser
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test Outbox
|
|
||||||
```bash
|
|
||||||
curl -H "Accept: application/activity+json" \
|
|
||||||
http://solar.local:5000/activitypub/actors/solaruser/outbox
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test Follow (from Mastodon)
|
|
||||||
1. Open http://mastodon.local:3001
|
|
||||||
2. Log in as `testuser@mastodon.local`
|
|
||||||
3. Search for `@solaruser@solar.local`
|
|
||||||
4. Click Follow
|
|
||||||
|
|
||||||
### Test Follow (from Solar Network to Mastodon)
|
|
||||||
```bash
|
|
||||||
# Send Follow activity to Solar Network
|
|
||||||
curl -X POST http://solar.local:5000/activitypub/actors/solaruser/inbox \
|
|
||||||
-H "Content-Type: application/activity+json" \
|
|
||||||
-d '{
|
|
||||||
"@context": "https://www.w3.org/ns/activitystreams",
|
|
||||||
"id": "http://solar.local:5000/follow-1",
|
|
||||||
"type": "Follow",
|
|
||||||
"actor": "https://solar.local:5000/activitypub/actors/solaruser",
|
|
||||||
"object": "http://mastodon.local:3001/users/testuser"
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Documentation Files
|
|
||||||
|
|
||||||
| File | Purpose |
|
|
||||||
|------|---------|
|
|
||||||
| `ACTIVITYPUB_TESTING_GUIDE.md` | Comprehensive testing guide |
|
|
||||||
| `ACTIVITYPUB_TESTING_QUICKREF.md` | Quick command reference |
|
|
||||||
| `ACTIVITYPUB_IMPLEMENTATION.md` | Implementation details |
|
|
||||||
| `ACTIVITYPUB_SUMMARY.md` | Feature summary |
|
|
||||||
| `ACTIVITYPUB_PLAN.md` | Original implementation plan |
|
|
||||||
|
|
||||||
## Database Checks
|
|
||||||
|
|
||||||
### Connect to Database
|
|
||||||
```bash
|
|
||||||
psql -d dyson_network
|
|
||||||
```
|
|
||||||
|
|
||||||
### View Actors
|
|
||||||
```sql
|
|
||||||
SELECT uri, username, display_name, created_at
|
|
||||||
FROM fediverse_actors;
|
|
||||||
```
|
|
||||||
|
|
||||||
### View Contents
|
|
||||||
```sql
|
|
||||||
SELECT uri, type, content, actor_id, created_at
|
|
||||||
FROM fediverse_contents
|
|
||||||
ORDER BY created_at DESC
|
|
||||||
LIMIT 10;
|
|
||||||
```
|
|
||||||
|
|
||||||
### View Relationships
|
|
||||||
```sql
|
|
||||||
SELECT state, is_following, is_followed_by, created_at
|
|
||||||
FROM fediverse_relationships;
|
|
||||||
```
|
|
||||||
|
|
||||||
### View Activities
|
|
||||||
```sql
|
|
||||||
SELECT type, status, error_message, created_at
|
|
||||||
FROM fediverse_activities
|
|
||||||
ORDER BY created_at DESC
|
|
||||||
LIMIT 10;
|
|
||||||
```
|
|
||||||
|
|
||||||
## Logs
|
|
||||||
|
|
||||||
### Solar Network Logs
|
|
||||||
```bash
|
|
||||||
# Live logs
|
|
||||||
dotnet run --project DysonNetwork.Sphere
|
|
||||||
|
|
||||||
# Follow ActivityPub activity
|
|
||||||
dotnet run --project DysonNetwork.Sphere 2>&1 | grep -i activitypub
|
|
||||||
|
|
||||||
# Debug logging
|
|
||||||
dotnet run --project DysonNetwork.Sphere --logging:LogLevel:DysonNetwork.Sphere.ActivityPub=Trace
|
|
||||||
```
|
|
||||||
|
|
||||||
### Mastodon Logs
|
|
||||||
```bash
|
|
||||||
# All services
|
|
||||||
docker compose -f docker-compose.mastodon-test.yml logs -f
|
|
||||||
|
|
||||||
# Web service only
|
|
||||||
docker compose -f docker-compose.mastodon-test.yml logs -f web
|
|
||||||
|
|
||||||
# Filter for federation
|
|
||||||
docker compose -f docker-compose.mastodon-test.yml logs -f web | grep -i federation
|
|
||||||
```
|
|
||||||
|
|
||||||
## Stopping Everything
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Stop Mastodon
|
|
||||||
docker compose -f docker-compose.mastodon-test.yml down
|
|
||||||
|
|
||||||
# Stop with volume cleanup
|
|
||||||
docker compose -f docker-compose.mastodon-test.yml down -v
|
|
||||||
|
|
||||||
# Restore /etc/hosts
|
|
||||||
sudo mv /etc/hosts.backup /etc/hosts
|
|
||||||
|
|
||||||
# Remove test databases (optional)
|
|
||||||
psql -d dyson_network <<EOF
|
|
||||||
TRUNCATE fediverse_activities CASCADE;
|
|
||||||
TRUNCATE fediverse_relationships CASCADE;
|
|
||||||
TRUNCATE fediverse_reactions CASCADE;
|
|
||||||
TRUNCATE fediverse_contents CASCADE;
|
|
||||||
TRUNCATE fediverse_actors CASCADE;
|
|
||||||
TRUNCATE fediverse_instances CASCADE;
|
|
||||||
UPDATE publishers SET meta = NULL WHERE meta IS NOT NULL;
|
|
||||||
EOF
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Mastodon won't start
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check logs
|
|
||||||
docker compose -f docker-compose.mastodon-test.yml logs -f web
|
|
||||||
|
|
||||||
# Restart
|
|
||||||
docker compose -f docker-compose.mastodon-test.yml restart
|
|
||||||
|
|
||||||
# Recreate
|
|
||||||
docker compose -f docker-compose.mastodon-test.yml down
|
|
||||||
docker compose -f docker-compose.mastodon-test.yml up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
### Can't connect to Solar Network
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check if running
|
|
||||||
curl http://solar.local:5000
|
|
||||||
|
|
||||||
# Check logs
|
|
||||||
dotnet run --project DysonNetwork.Sphere 2>&1 | grep -i error
|
|
||||||
|
|
||||||
# Restart
|
|
||||||
# Ctrl+C in terminal and run again
|
|
||||||
```
|
|
||||||
|
|
||||||
### Activities not arriving
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check database
|
|
||||||
psql -d dyson_network -c "SELECT * FROM fediverse_activities WHERE status = 3;"
|
|
||||||
|
|
||||||
# Check signature verification logs
|
|
||||||
dotnet run --project DysonNetwork.Sphere 2>&1 | grep -i "signature"
|
|
||||||
|
|
||||||
# Verify actor keys
|
|
||||||
curl -H "Accept: application/activity+json" \
|
|
||||||
http://solar.local:5000/activitypub/actors/solaruser | jq '.publicKey'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing Checklist
|
|
||||||
|
|
||||||
- [ ] Setup script completed successfully
|
|
||||||
- [ ] Mastodon is running and accessible
|
|
||||||
- [ ] Solar Network is running and accessible
|
|
||||||
- [ ] WebFinger returns correct data
|
|
||||||
- [ ] Actor profile includes public key
|
|
||||||
- [ ] Follow from Mastodon to Solar Network works
|
|
||||||
- [ ] Follow from Solar Network to Mastodon works
|
|
||||||
- [ ] Posts from Solar Network appear in Mastodon
|
|
||||||
- [ ] Posts from Mastodon appear in Solar Network database
|
|
||||||
- [ ] Likes federate correctly
|
|
||||||
- [ ] Replies federate correctly
|
|
||||||
- [ ] HTTP signatures are verified
|
|
||||||
- [ ] No errors in logs
|
|
||||||
- [ ] Database contains expected data
|
|
||||||
|
|
||||||
## Next Steps
|
|
||||||
|
|
||||||
1. **Test with a real instance**:
|
|
||||||
- Get a public domain or use ngrok
|
|
||||||
- Update `ActivityPub:Domain` in appsettings.json
|
|
||||||
- Test with mastodon.social or other public instances
|
|
||||||
|
|
||||||
2. **Add more features**:
|
|
||||||
- Activity queue for async processing
|
|
||||||
- Retry logic for failed deliveries
|
|
||||||
- Metrics and monitoring
|
|
||||||
- Admin interface for federation management
|
|
||||||
|
|
||||||
3. **Test with more instances**:
|
|
||||||
- Pleroma
|
|
||||||
- Pixelfed
|
|
||||||
- Lemmy
|
|
||||||
- PeerTube
|
|
||||||
|
|
||||||
## Getting Help
|
|
||||||
|
|
||||||
If something doesn't work:
|
|
||||||
|
|
||||||
1. Check the logs (see Logs section above)
|
|
||||||
2. Review the troubleshooting section in [ACTIVITYPUB_TESTING_GUIDE.md](ACTIVITYPUB_TESTING_GUIDE.md)
|
|
||||||
3. Verify all prerequisites are installed
|
|
||||||
4. Check network connectivity between instances
|
|
||||||
5. Review the [ACTIVITYPUB_IMPLEMENTATION.md](ACTIVITYPUB_IMPLEMENTATION.md) for architecture details
|
|
||||||
|
|
||||||
## Useful URLs
|
|
||||||
|
|
||||||
### Test Instances
|
|
||||||
- Mastodon: http://mastodon.local:3001
|
|
||||||
- Solar Network: http://solar.local:5000
|
|
||||||
|
|
||||||
### Documentation
|
|
||||||
- ActivityPub W3C Spec: https://www.w3.org/TR/activitypub/
|
|
||||||
- Mastodon Federation Docs: https://docs.joinmastodon.org/admin/federation/
|
|
||||||
- ActivityPub Playground: https://swicth.github.io/activity-pub-playground/
|
|
||||||
|
|
||||||
### Tools
|
|
||||||
- jq: JSON processor (https://stedolan.github.io/jq/)
|
|
||||||
- httpie: HTTP client (https://httpie.io/)
|
|
||||||
- Docker Compose: (https://docs.docker.com/compose/)
|
|
||||||
@@ -1,275 +0,0 @@
|
|||||||
# ActivityPub Testing Guide
|
|
||||||
|
|
||||||
Complete guide for testing ActivityPub federation in Solar Network.
|
|
||||||
|
|
||||||
## 📚 Documentation Files
|
|
||||||
|
|
||||||
| File | Description | Size |
|
|
||||||
|------|-------------|-------|
|
|
||||||
| `ACTIVITYPUB_TESTING_INDEX.md` | **START HERE** - Master guide with overview | 12K |
|
|
||||||
| `ACTIVITYPUB_TESTING_QUICKSTART.md` | Quick reference for common tasks | 7K |
|
|
||||||
| `ACTIVITYPUB_TESTING_GUIDE.md` | Comprehensive testing scenarios (10 parts) | 19K |
|
|
||||||
| `ACTIVITYPUB_TESTING_QUICKREF.md` | Command and query reference | 8K |
|
|
||||||
| `ACTIVITYPUB_TESTING_HELPER_API.md` | Helper API for programmatic testing | 12K |
|
|
||||||
| `ACTIVITYPUB_TESTING_RESULTS_TEMPLATE.md` | Template to track test results | 10K |
|
|
||||||
|
|
||||||
## 🚀 Quick Start
|
|
||||||
|
|
||||||
### Option A: One-Command Setup (Recommended)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. Run setup script
|
|
||||||
./setup-activitypub-test.sh
|
|
||||||
|
|
||||||
# 2. Run validation
|
|
||||||
./test-activitypub.sh
|
|
||||||
|
|
||||||
# 3. Start Solar Network
|
|
||||||
cd DysonNetwork.Sphere
|
|
||||||
dotnet run
|
|
||||||
```
|
|
||||||
|
|
||||||
### Option B: Manual Setup
|
|
||||||
|
|
||||||
1. **Read**: `ACTIVITYPUB_TESTING_QUICKSTART.md`
|
|
||||||
2. **Configure**: Copy `.env.testing.example` to `.env` and adjust
|
|
||||||
3. **Follow**: Step-by-step in `ACTIVITYPUB_TESTING_GUIDE.md`
|
|
||||||
|
|
||||||
## 🎯 What You Can Test
|
|
||||||
|
|
||||||
### With Self-Hosted Instance
|
|
||||||
- ✅ WebFinger discovery
|
|
||||||
- ✅ Actor profile retrieval
|
|
||||||
- ✅ Follow relationships (bidirectional)
|
|
||||||
- ✅ Post federation (Solar → Mastodon)
|
|
||||||
- ✅ Content reception (Mastodon → Solar)
|
|
||||||
- ✅ Like interactions
|
|
||||||
- ✅ Reply threading
|
|
||||||
- ✅ HTTP signature verification
|
|
||||||
- ✅ Content deletion
|
|
||||||
|
|
||||||
### With Real Instance
|
|
||||||
- ✅ Public domain setup (via ngrok or VPS)
|
|
||||||
- ✅ Federation with public instances (mastodon.social, etc.)
|
|
||||||
- ✅ Real-world compatibility testing
|
|
||||||
- ✅ Performance under real load
|
|
||||||
|
|
||||||
## 📋 Testing Workflow
|
|
||||||
|
|
||||||
### Day 1: Basic Functionality
|
|
||||||
- Setup test environment
|
|
||||||
- Test WebFinger and Actor endpoints
|
|
||||||
- Verify HTTP signatures
|
|
||||||
- Test basic follow/unfollow
|
|
||||||
|
|
||||||
### Day 2: Content Federation
|
|
||||||
- Test post creation and delivery
|
|
||||||
- Test content reception
|
|
||||||
- Test media attachments
|
|
||||||
- Test content warnings
|
|
||||||
|
|
||||||
### Day 3: Interactions
|
|
||||||
- Test likes (both directions)
|
|
||||||
- Test replies and threading
|
|
||||||
- Test boosts/announces
|
|
||||||
- Test undo activities
|
|
||||||
|
|
||||||
### Day 4: Real Instance
|
|
||||||
- Set up public domain
|
|
||||||
- Test with mastodon.social
|
|
||||||
- Test with other instances
|
|
||||||
- Verify cross-instance compatibility
|
|
||||||
|
|
||||||
### Day 5: Edge Cases
|
|
||||||
- Test error handling
|
|
||||||
- Test failed deliveries
|
|
||||||
- Test invalid signatures
|
|
||||||
- Test malformed activities
|
|
||||||
|
|
||||||
## 🛠️ Setup Scripts
|
|
||||||
|
|
||||||
| Script | Purpose |
|
|
||||||
|--------|---------|
|
|
||||||
| `setup-activitypub-test.sh` | One-command setup of Mastodon + Solar Network |
|
|
||||||
| `test-activitypub.sh` | Quick validation of core functionality |
|
|
||||||
|
|
||||||
Both scripts are executable (`chmod +x`).
|
|
||||||
|
|
||||||
## 🔧 Configuration
|
|
||||||
|
|
||||||
### Required Tools
|
|
||||||
- ✅ Docker (for Mastodon)
|
|
||||||
- ✅ .NET 10 SDK (for Solar Network)
|
|
||||||
- ✅ PostgreSQL client (psql)
|
|
||||||
- ✅ curl (for API testing)
|
|
||||||
|
|
||||||
### Quick Setup
|
|
||||||
```bash
|
|
||||||
# 1. Install dependencies (Ubuntu/Debian)
|
|
||||||
sudo apt-get install docker.io docker-compose postgresql-client curl jq
|
|
||||||
|
|
||||||
# 2. Run setup
|
|
||||||
./setup-activitypub-test.sh
|
|
||||||
|
|
||||||
# 3. Validate
|
|
||||||
./test-activitypub.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📊 Progress Tracking
|
|
||||||
|
|
||||||
Use the template to track your testing progress:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Copy the template
|
|
||||||
cp ACTIVITYPUB_TESTING_RESULTS_TEMPLATE.md my-test-results.md
|
|
||||||
|
|
||||||
# Edit as you test
|
|
||||||
nano my-test-results.md
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🐛 Troubleshooting
|
|
||||||
|
|
||||||
### Quick Fixes
|
|
||||||
|
|
||||||
**Mastodon won't start**:
|
|
||||||
```bash
|
|
||||||
# Check logs
|
|
||||||
docker compose -f docker-compose.mastodon-test.yml logs -f
|
|
||||||
|
|
||||||
# Restart containers
|
|
||||||
docker compose -f docker-compose.mastodon-test.yml restart
|
|
||||||
```
|
|
||||||
|
|
||||||
**Can't reach Solar Network**:
|
|
||||||
```bash
|
|
||||||
# Check if running
|
|
||||||
curl http://solar.local:5000
|
|
||||||
|
|
||||||
# Check /etc/hosts
|
|
||||||
cat /etc/hosts | grep solar.local
|
|
||||||
```
|
|
||||||
|
|
||||||
**Activities not arriving**:
|
|
||||||
```bash
|
|
||||||
# Check database
|
|
||||||
psql -d dyson_network -c "SELECT * FROM fediverse_activities;"
|
|
||||||
|
|
||||||
# Check logs
|
|
||||||
dotnet run --project DysonNetwork.Sphere | grep -i activitypub
|
|
||||||
```
|
|
||||||
|
|
||||||
For detailed troubleshooting, see `ACTIVITYPUB_TESTING_GUIDE.md` Part 5.
|
|
||||||
|
|
||||||
## 📖 Learning Path
|
|
||||||
|
|
||||||
### For Developers
|
|
||||||
1. Read `ACTIVITYPUB_IMPLEMENTATION.md` to understand the architecture
|
|
||||||
2. Read `ACTIVITYPUB_SUMMARY.md` to see what's implemented
|
|
||||||
3. Follow test scenarios in `ACTIVITYPUB_TESTING_GUIDE.md`
|
|
||||||
4. Use helper API in `ACTIVITYPUB_TESTING_HELPER_API.md` for testing
|
|
||||||
|
|
||||||
### For Testers
|
|
||||||
1. Start with `ACTIVITYPUB_TESTING_QUICKSTART.md`
|
|
||||||
2. Use command reference in `ACTIVITYPUB_TESTING_QUICKREF.md`
|
|
||||||
3. Track results with `ACTIVITYPUB_TESTING_RESULTS_TEMPLATE.md`
|
|
||||||
4. Report issues with details from logs
|
|
||||||
|
|
||||||
## 🎓 Success Criteria
|
|
||||||
|
|
||||||
### Minimum Viable
|
|
||||||
- WebFinger works
|
|
||||||
- Actor profile valid
|
|
||||||
- Follow relationships work
|
|
||||||
- Posts federate correctly
|
|
||||||
- HTTP signatures verified
|
|
||||||
|
|
||||||
### Production Ready
|
|
||||||
- Activity queue with retry
|
|
||||||
- Rate limiting
|
|
||||||
- Monitoring/alerting
|
|
||||||
- Admin interface
|
|
||||||
- Instance blocking
|
|
||||||
- Content moderation
|
|
||||||
|
|
||||||
## 🚨 Common Pitfalls
|
|
||||||
|
|
||||||
### Don't Forget
|
|
||||||
- ✅ Update `/etc/hosts` with both instances
|
|
||||||
- ✅ Run migrations before testing
|
|
||||||
- ✅ Check both instances are accessible
|
|
||||||
- ✅ Verify PostgreSQL is running
|
|
||||||
- ✅ Check logs when something fails
|
|
||||||
|
|
||||||
### Watch Out For
|
|
||||||
- ❌ Using `localhost` instead of `solar.local`
|
|
||||||
- ❌ Forgetting to restart after config changes
|
|
||||||
- ❌ Not waiting for Mastodon to start (2-5 minutes)
|
|
||||||
- ❌ Ignoring CORS errors in browser testing
|
|
||||||
- ❌ Testing with deleted/invisible posts
|
|
||||||
|
|
||||||
## 📚 Additional Resources
|
|
||||||
|
|
||||||
### Official Specs
|
|
||||||
- [ActivityPub W3C](https://www.w3.org/TR/activitypub/)
|
|
||||||
- [ActivityStreams](https://www.w3.org/TR/activitystreams-core/)
|
|
||||||
- [HTTP Signatures](https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures)
|
|
||||||
|
|
||||||
### Community Guides
|
|
||||||
- [Mastodon Federation](https://docs.joinmastodon.org/admin/federation/)
|
|
||||||
- [Federation Testing](https://docs.joinmastodon.org/spec/activitypub/)
|
|
||||||
|
|
||||||
### Tools
|
|
||||||
- [ActivityPub Playground](https://swicth.github.io/activity-pub-playground/)
|
|
||||||
- [FediTest](https://feditest.com/)
|
|
||||||
|
|
||||||
## 🆘 Support
|
|
||||||
|
|
||||||
If you encounter issues:
|
|
||||||
|
|
||||||
1. Check logs (both Solar Network and Mastodon)
|
|
||||||
2. Review troubleshooting section in the guide
|
|
||||||
3. Validate against success criteria
|
|
||||||
4. Check database state with queries
|
|
||||||
5. Review implementation docs
|
|
||||||
|
|
||||||
## ✨ Next Steps
|
|
||||||
|
|
||||||
After testing with self-hosted instance:
|
|
||||||
|
|
||||||
1. Get a public domain or use ngrok
|
|
||||||
2. Update `ActivityPub:Domain` in appsettings.json
|
|
||||||
3. Test with public Mastodon instances
|
|
||||||
4. Add more ActivityPub features (queue, retry, etc.)
|
|
||||||
5. Implement admin interface
|
|
||||||
6. Add monitoring and metrics
|
|
||||||
|
|
||||||
## 📞 File Reference
|
|
||||||
|
|
||||||
All files are in the root of the DysonNetwork project:
|
|
||||||
|
|
||||||
```
|
|
||||||
DysonNetwork/
|
|
||||||
├── ACTIVITYPUB_TESTING_INDEX.md # Start here!
|
|
||||||
├── ACTIVITYPUB_TESTING_QUICKSTART.md # Quick reference
|
|
||||||
├── ACTIVITYPUB_TESTING_GUIDE.md # Full guide
|
|
||||||
├── ACTIVITYPUB_TESTING_QUICKREF.md # Commands
|
|
||||||
├── ACTIVITYPUB_TESTING_HELPER_API.md # Test API
|
|
||||||
├── ACTIVITYPUB_TESTING_RESULTS_TEMPLATE.md
|
|
||||||
├── setup-activitypub-test.sh # Setup script
|
|
||||||
├── test-activitypub.sh # Test script
|
|
||||||
└── .env.testing.example # Config template
|
|
||||||
```
|
|
||||||
|
|
||||||
**Documentation files** (for reference):
|
|
||||||
```
|
|
||||||
DysonNetwork/
|
|
||||||
├── ACTIVITYPUB_IMPLEMENTATION.md # How it's implemented
|
|
||||||
├── ACTIVITYPUB_SUMMARY.md # Feature summary
|
|
||||||
└── ACTIVITYPUB_PLAN.md # Original plan
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Start here**: `ACTIVITYPUB_TESTING_INDEX.md`
|
|
||||||
|
|
||||||
**Good luck with your testing!** 🚀
|
|
||||||
@@ -1,282 +0,0 @@
|
|||||||
# ActivityPub Testing Results Template
|
|
||||||
|
|
||||||
Use this template to track your testing progress.
|
|
||||||
|
|
||||||
## Test Environment
|
|
||||||
|
|
||||||
**Date**: ________________
|
|
||||||
|
|
||||||
**Test Configuration**:
|
|
||||||
- Solar Network URL: `http://solar.local:5000`
|
|
||||||
- Mastodon URL: `http://mastodon.local:3001`
|
|
||||||
- Database: `dyson_network`
|
|
||||||
|
|
||||||
**Solar Network User**:
|
|
||||||
- Username: `_______________`
|
|
||||||
- Publisher ID: `_______________`
|
|
||||||
|
|
||||||
**Mastodon User**:
|
|
||||||
- Username: `testuser@mastodon.local`
|
|
||||||
- Password: `TestPassword123!`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Test Results
|
|
||||||
|
|
||||||
### ✅ Part 1: Infrastructure Setup
|
|
||||||
|
|
||||||
| Test | Status | Notes |
|
|
||||||
|------|--------|-------|
|
|
||||||
| Setup script ran successfully | ☐ ☑ | |
|
|
||||||
| /etc/hosts updated | ☐ ☑ | |
|
|
||||||
| Docker containers started | ☐ ☑ | |
|
|
||||||
| Mastodon web accessible | ☐ ☑ | |
|
|
||||||
| Mastodon admin account created | ☐ ☑ | |
|
|
||||||
| Database migrations applied | ☐ ☑ | |
|
|
||||||
| Solar Network started | ☐ ☑ | |
|
|
||||||
|
|
||||||
### ✅ Part 2: WebFinger & Actor Discovery
|
|
||||||
|
|
||||||
| Test | Status | Expected | Actual |
|
|
||||||
|------|--------|---------|--------|
|
|
||||||
| WebFinger for Solar Network user | ☐ ☑ | Returns subject + links | _______________ |
|
|
||||||
| Actor profile JSON is valid | ☐ ☑ | Has id, type, inbox, outbox | _______________ |
|
|
||||||
| Public key present in actor | ☐ ☑ | publicKey.publicKeyPem exists | _______________ |
|
|
||||||
| Outbox returns public posts | ☐ ☑ | OrderedCollection with items | _______________ |
|
|
||||||
| Outbox totalItems count | ☐ ☑ | Matches public posts | _______________ |
|
|
||||||
|
|
||||||
### ✅ Part 3: Follow Relationships
|
|
||||||
|
|
||||||
| Test | Status | Expected Result | Actual Result |
|
|
||||||
|------|--------|----------------|---------------|
|
|
||||||
| Mastodon follows Solar Network user | ☐ ☑ | Relationship created in DB | _______________ |
|
|
||||||
| Accept sent to Mastodon | ☐ ☑ | Mastodon receives Accept | _______________ |
|
|
||||||
| Solar Network follows Mastodon user | ☐ ☑ | Relationship created | _______________ |
|
|
||||||
| Follow appears in Mastodon UI | ☐ ☑ | Mastodon shows "Following" | _______________ |
|
|
||||||
| Follow appears in Solar Network DB | ☐ ☑ | is_following = true | _______________ |
|
|
||||||
| Follow state is Accepted | ☐ ☑ | state = 1 (Accepted) | _______________ |
|
|
||||||
| Unfollow works correctly | ☐ ☑ | Relationship deleted/updated | _______________ |
|
|
||||||
|
|
||||||
### ✅ Part 4: Content Federation (Create)
|
|
||||||
|
|
||||||
| Test | Status | Expected Result | Actual Result |
|
|
||||||
|------|--------|----------------|---------------|
|
|
||||||
| Post created in Solar Network | ☐ ☑ | Post in sn_posts table | _______________ |
|
|
||||||
| Activity sent to Mastodon | ☐ ☑ | Logged as successful | _______________ |
|
|
||||||
| Post appears in Mastodon timeline | ☐ ☑ | Visible in federated timeline | _______________ |
|
|
||||||
| Post content matches | ☐ ☑ | Same text/HTML | _______________ |
|
|
||||||
| Post author is correct | ☐ ☑ | Shows Solar Network user | _______________ |
|
|
||||||
| Post timestamp is correct | ☐ ☑ | Same published time | _______________ |
|
|
||||||
| Multiple posts federate | ☐ ☑ | All posts appear | _______________ |
|
|
||||||
|
|
||||||
### ✅ Part 5: Content Reception (Incoming Create)
|
|
||||||
|
|
||||||
| Test | Status | Expected Result | Actual Result |
|
|
||||||
|------|--------|----------------|---------------|
|
|
||||||
| Create activity received | ☐ ☑ | Activity logged in DB | _______________ |
|
|
||||||
| Content stored in fediverse_contents | ☐ ☑ | Record with correct type | _______________ |
|
|
||||||
| Content not duplicated | ☐ ☑ | Only one entry per URI | _______________ |
|
|
||||||
| Actor created/retrieved | ☐ ☑ | Actor in fediverse_actors | _______________ |
|
|
||||||
| Instance created/retrieved | ☐ ☑ | Instance in fediverse_instances | _______________ |
|
|
||||||
| Content HTML preserved | ☐ ☑ | contentHtml field populated | _______________ |
|
|
||||||
|
|
||||||
### ✅ Part 6: Reaction Federation (Like)
|
|
||||||
|
|
||||||
| Test | Status | Expected Result | Actual Result |
|
|
||||||
|------|--------|----------------|---------------|
|
|
||||||
| Like from Mastodon to Solar post | ☐ ☑ | Like activity received | _______________ |
|
|
||||||
| Reaction stored in fediverse_reactions | ☐ ☑ | Record with type = 0 (Like) | _______________ |
|
|
||||||
| Like count incremented | ☐ ☑ | like_count increased | _______________ |
|
|
||||||
| Like appears in UI | ☐ ☑ | Visible on Solar Network | _______________ |
|
|
||||||
| Like appears in Mastodon | ☐ ☑ | Visible on Mastodon | _______________ |
|
|
||||||
| Unlike works correctly | ☐ ☑ | Like removed | _______________ |
|
|
||||||
|
|
||||||
### ✅ Part 7: Reply Federation
|
|
||||||
|
|
||||||
| Test | Status | Expected Result | Actual Result |
|
|
||||||
|------|--------|----------------|---------------|
|
|
||||||
| Reply from Mastodon to Solar post | ☐ ☑ | Create activity with inReplyTo | _______________ |
|
|
||||||
| Reply stored with parent reference | ☐ ☑ | in_reply_to field set | _______________ |
|
|
||||||
| Reply appears in Solar Network | ☐ ☑ | Visible as comment | _______________ |
|
|
||||||
| Reply shows parent context | ☐ ☑ | Links to original post | _______________ |
|
|
||||||
|
|
||||||
### ✅ Part 8: Content Deletion
|
|
||||||
|
|
||||||
| Test | Status | Expected Result | Actual Result |
|
|
||||||
|------|--------|----------------|---------------|
|
|
||||||
| Delete from Mastodon | ☐ ☑ | Delete activity received | _______________ |
|
|
||||||
| Content soft-deleted | ☐ ☑ | deleted_at timestamp set | _______________ |
|
|
||||||
| Content no longer visible | ☐ ☑ | Hidden from timelines | _______________ |
|
|
||||||
|
|
||||||
### ✅ Part 9: HTTP Signature Verification
|
|
||||||
|
|
||||||
| Test | Status | Expected Result | Actual Result |
|
|
||||||
|------|--------|----------------|---------------|
|
|
||||||
| Valid signature accepted | ☐ ☑ | Activity processed | _______________ |
|
|
||||||
| Invalid signature rejected | ☐ ☑ | 401 Unauthorized | _______________ |
|
|
||||||
| Missing signature rejected | ☐ ☑ | 401 Unauthorized | _______________ |
|
|
||||||
| Signature format correct | ☐ ☑ | keyId, algorithm, headers, signature | _______________ |
|
|
||||||
| Signing string correct | ☐ ☑ | Matches HTTP-Signatures draft | _______________ |
|
|
||||||
|
|
||||||
### ✅ Part 10: Error Handling
|
|
||||||
|
|
||||||
| Test | Status | Expected Result | Actual Result |
|
|
||||||
|------|--------|----------------|---------------|
|
|
||||||
| Invalid activity type rejected | ☐ ☑ | 400 Bad Request | _______________ |
|
|
||||||
| Malformed JSON rejected | ☐ ☑ | 400 Bad Request | _______________ |
|
|
||||||
| Non-existent actor rejected | ☐ ☑ | 404 Not Found | _______________ |
|
|
||||||
| Errors logged correctly | ☐ ☑ | error_message populated | _______________ |
|
|
||||||
| Activity status = Failed | ☐ ☑ | status = 3 | _______________ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Database State After Tests
|
|
||||||
|
|
||||||
### Actors Table
|
|
||||||
```sql
|
|
||||||
SELECT COUNT(*) as total_actors,
|
|
||||||
SUM(CASE WHEN is_local_actor THEN 1 ELSE 0 END) as local,
|
|
||||||
SUM(CASE WHEN NOT is_local_actor THEN 1 ELSE 0 END) as remote
|
|
||||||
FROM fediverse_relationships;
|
|
||||||
```
|
|
||||||
- Total Actors: _______________
|
|
||||||
- Local Actors: _______________
|
|
||||||
- Remote Actors: _______________
|
|
||||||
|
|
||||||
### Contents Table
|
|
||||||
```sql
|
|
||||||
SELECT COUNT(*) as total_contents,
|
|
||||||
AVG(LENGTH(content)) as avg_content_length
|
|
||||||
FROM fediverse_contents WHERE deleted_at IS NULL;
|
|
||||||
```
|
|
||||||
- Total Contents: _______________
|
|
||||||
- Avg Content Length: _______________
|
|
||||||
|
|
||||||
### Activities Table
|
|
||||||
```sql
|
|
||||||
SELECT type, status, COUNT(*)
|
|
||||||
FROM fediverse_activities
|
|
||||||
GROUP BY type, status
|
|
||||||
ORDER BY type, status;
|
|
||||||
```
|
|
||||||
- Activities by Type/Status:
|
|
||||||
- Create: Pending ___, Completed ____, Failed ___
|
|
||||||
- Follow: Pending ___, Completed ____, Failed ___
|
|
||||||
- Like: Pending ___, Completed ____, Failed ___
|
|
||||||
- Accept: Pending ___, Completed ____, Failed ___
|
|
||||||
|
|
||||||
### Relationships Table
|
|
||||||
```sql
|
|
||||||
SELECT state, COUNT(*) as count
|
|
||||||
FROM fediverse_relationships
|
|
||||||
GROUP BY state;
|
|
||||||
```
|
|
||||||
- Pending: _______________
|
|
||||||
- Accepted: _______________
|
|
||||||
- Rejected: _______________
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Logs Analysis
|
|
||||||
|
|
||||||
### Solar Network Errors Found:
|
|
||||||
1. _______________
|
|
||||||
2. _______________
|
|
||||||
3. _______________
|
|
||||||
|
|
||||||
### Mastodon Errors Found:
|
|
||||||
1. _______________
|
|
||||||
2. _______________
|
|
||||||
3. _______________
|
|
||||||
|
|
||||||
### Warnings Found:
|
|
||||||
1. _______________
|
|
||||||
2. _______________
|
|
||||||
3. _______________
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Issues & Bugs Found
|
|
||||||
|
|
||||||
| # | Severity | Description | Status |
|
|
||||||
|---|----------|-------------|--------|
|
|
||||||
| 1 | ☐ Low/Medium/High/Critical | _____________________ | ☐ Open/☐ Fixed |
|
|
||||||
| 2 | ☐ Low/Medium/High/Critical | _____________________ | ☐ Open/☐ Fixed |
|
|
||||||
| 3 | ☐ Low/Medium/High/Critical | _____________________ | ☐ Open/☐ Fixed |
|
|
||||||
| 4 | ☐ Low/Medium/High/Critical | _____________________ | ☐ Open/☐ Fixed |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Performance Notes
|
|
||||||
|
|
||||||
| Metric | Value | Notes |
|
|
||||||
|--------|-------|-------|
|
|
||||||
| Average activity processing time | __________ ms | |
|
|
||||||
| Average HTTP signature verification time | __________ ms | |
|
|
||||||
| Outgoing delivery success rate | __________% | |
|
|
||||||
| Average WebFinger response time | __________ ms | |
|
|
||||||
| Database query performance | __________ | |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Compatibility Notes
|
|
||||||
|
|
||||||
| Instance | Version | Works | Notes |
|
|
||||||
|----------|---------|--------|-------|
|
|
||||||
| Mastodon (self-hosted) | latest | ☐ ☑ | |
|
|
||||||
| Mastodon.social | ~4.0 | ☐ ☑ | |
|
|
||||||
| Pleroma | ~2.5 | ☐ ☑ | |
|
|
||||||
| GoToSocial | ~0.15 | ☐ ☑ | |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Recommendations
|
|
||||||
|
|
||||||
### What Worked Well:
|
|
||||||
1. _____________________
|
|
||||||
2. _____________________
|
|
||||||
3. _____________________
|
|
||||||
|
|
||||||
### What Needs Improvement:
|
|
||||||
1. _____________________
|
|
||||||
2. _____________________
|
|
||||||
3. _____________________
|
|
||||||
|
|
||||||
### Features to Add:
|
|
||||||
1. _____________________
|
|
||||||
2. _____________________
|
|
||||||
3. _____________________
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Next Testing Phase
|
|
||||||
|
|
||||||
- [ ] Test with public Mastodon instance
|
|
||||||
- [ ] Test with Pleroma instance
|
|
||||||
- [ ] Test media attachment federation
|
|
||||||
- [ ] Test with high-volume posts
|
|
||||||
- [ ] Test concurrent activity processing
|
|
||||||
- [ ] Test with different visibility levels
|
|
||||||
- [ ] Test with long posts (>500 chars)
|
|
||||||
- [ ] Test with special characters/emojis
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Sign-off
|
|
||||||
|
|
||||||
**Tested By**: _____________________
|
|
||||||
|
|
||||||
**Test Date**: _____________________
|
|
||||||
|
|
||||||
**Overall Result**: ☐ Pass / ☐ Fail
|
|
||||||
|
|
||||||
**Ready for Production**: ☐ Yes / ☐ No
|
|
||||||
|
|
||||||
**Notes**: ___________________________________________________________________________
|
|
||||||
|
|
||||||
__________________________________________________________________________
|
|
||||||
|
|
||||||
__________________________________________________________________________
|
|
||||||
|
|
||||||
__________________________________________________________________________
|
|
||||||
|
|
||||||
@@ -1,425 +0,0 @@
|
|||||||
# Follow Feature - User Guide
|
|
||||||
|
|
||||||
## Quick Start: How to Follow Fediverse Users
|
|
||||||
|
|
||||||
### Method 1: Via Search (Recommended)
|
|
||||||
|
|
||||||
1. Go to the search bar in Solar Network
|
|
||||||
2. Type the user's full address: `@username@domain.com`
|
|
||||||
- Example: `@alice@mastodon.social`
|
|
||||||
- Example: `@bob@pleroma.site`
|
|
||||||
3. Click on their profile in search results
|
|
||||||
4. Click the "Follow" button
|
|
||||||
5. Wait for acceptance (usually immediate)
|
|
||||||
6. ✅ Done! Their posts will now appear in your timeline
|
|
||||||
|
|
||||||
### Method 2: Via Profile URL
|
|
||||||
|
|
||||||
1. If you know their profile URL, visit it directly:
|
|
||||||
- Example: `https://mastodon.social/@alice`
|
|
||||||
2. Look for the "Follow" button on their profile
|
|
||||||
3. Click it to follow
|
|
||||||
4. ✅ You're now following them!
|
|
||||||
|
|
||||||
## What Happens When You Follow Someone
|
|
||||||
|
|
||||||
### The Technical Flow
|
|
||||||
```
|
|
||||||
You click "Follow"
|
|
||||||
↓
|
|
||||||
Solar Network creates Follow Activity
|
|
||||||
↓
|
|
||||||
Follow Activity is signed with your private key
|
|
||||||
↓
|
|
||||||
Solar Network sends Follow to their instance's inbox
|
|
||||||
↓
|
|
||||||
Their instance verifies your signature
|
|
||||||
↓
|
|
||||||
Their instance processes the Follow
|
|
||||||
↓
|
|
||||||
Their instance sends Accept Activity back
|
|
||||||
↓
|
|
||||||
Solar Network receives and processes Accept
|
|
||||||
↓
|
|
||||||
Relationship is stored in database
|
|
||||||
↓
|
|
||||||
Their public posts federate to Solar Network
|
|
||||||
```
|
|
||||||
|
|
||||||
### What You'll See
|
|
||||||
|
|
||||||
- ✅ **"Following..."** (while waiting for acceptance)
|
|
||||||
- ✅ **"Following" ✓** (when accepted)
|
|
||||||
- ✅ **Their posts** in your home timeline
|
|
||||||
- ✅ **Their likes, replies, boosts** on your posts
|
|
||||||
|
|
||||||
## Different Types of Accounts
|
|
||||||
|
|
||||||
### Regular Users
|
|
||||||
- Full ActivityPub support
|
|
||||||
- Follows work both ways
|
|
||||||
- Content federates normally
|
|
||||||
- Example: `@alice@mastodon.social`
|
|
||||||
|
|
||||||
### Locked Accounts
|
|
||||||
- User must manually approve followers
|
|
||||||
- You'll see "Pending" after clicking follow
|
|
||||||
- User receives notification to approve/deny
|
|
||||||
- Example: `@private@pleroma.site`
|
|
||||||
|
|
||||||
### Bot/Service Accounts
|
|
||||||
- Automated content accounts
|
|
||||||
- Often auto-accept follows
|
|
||||||
- Example: `@newsbot@botsin.space`
|
|
||||||
|
|
||||||
### Organizational Accounts
|
|
||||||
- Group or team accounts
|
|
||||||
- Example: `@team@company.social`
|
|
||||||
|
|
||||||
## Managing Your Follows
|
|
||||||
|
|
||||||
### View Who You're Following
|
|
||||||
|
|
||||||
**Go to**: Following page or `GET /api/activitypub/following`
|
|
||||||
|
|
||||||
You'll see:
|
|
||||||
- Username
|
|
||||||
- Display name
|
|
||||||
- Profile picture
|
|
||||||
- When you followed them
|
|
||||||
- Their instance (e.g., "Mastodon")
|
|
||||||
|
|
||||||
### Unfollowing Someone
|
|
||||||
|
|
||||||
**Method 1: Via UI**
|
|
||||||
1. Go to their profile
|
|
||||||
2. Click "Following" button (shows as active)
|
|
||||||
3. Click to unfollow
|
|
||||||
|
|
||||||
**Method 2: Via API**
|
|
||||||
```bash
|
|
||||||
curl -X POST http://solar.local:5000/api/activitypub/unfollow \
|
|
||||||
-H "Authorization: Bearer YOUR_TOKEN" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{
|
|
||||||
"targetActorUri": "https://mastodon.social/users/alice"
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"message": "Unfollowed successfully"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### View Your Followers
|
|
||||||
|
|
||||||
**Go to**: Followers page or `GET /api/activitypub/followers`
|
|
||||||
|
|
||||||
You'll see:
|
|
||||||
- Users following you
|
|
||||||
- Their instance
|
|
||||||
- When they started following
|
|
||||||
- Whether they're local or from another instance
|
|
||||||
|
|
||||||
## Searching Fediverse Users
|
|
||||||
|
|
||||||
### How Search Works
|
|
||||||
|
|
||||||
1. **Type in search bar**: `@username@domain.com`
|
|
||||||
2. **Solar Network queries their instance**:
|
|
||||||
- Fetches their actor profile
|
|
||||||
- Checks if they're discoverable
|
|
||||||
3. **Shows results**:
|
|
||||||
- Profile picture
|
|
||||||
- Display name
|
|
||||||
- Bio
|
|
||||||
- Instance name
|
|
||||||
|
|
||||||
### Supported Search Formats
|
|
||||||
|
|
||||||
| Format | Example | Works? |
|
|
||||||
|--------|---------|--------|
|
|
||||||
| Full handle | `@alice@mastodon.social` | ✅ Yes |
|
|
||||||
| Username only | `alice` | ⚠️ May search local users first |
|
|
||||||
| Full URL | `https://mastodon.social/@alice` | ✅ Yes |
|
|
||||||
|
|
||||||
## Privacy Considerations
|
|
||||||
|
|
||||||
### Public Posts
|
|
||||||
- **What**: Posts visible to everyone
|
|
||||||
- **Federation**: ✅ Federates to all followers
|
|
||||||
- **Timeline**: Visible in public federated timelines
|
|
||||||
- **Example**: General updates, thoughts, content you want to share
|
|
||||||
|
|
||||||
### Private Posts
|
|
||||||
- **What**: Posts only visible to followers
|
|
||||||
- **Federation**: ✅ Federates to followers (including remote)
|
|
||||||
- **Timeline**: Only visible to your followers
|
|
||||||
- **Example**: Personal updates, questions
|
|
||||||
|
|
||||||
### Unlisted Posts
|
|
||||||
- **What**: Posts not in public timelines
|
|
||||||
- **Federation**: ✅ Federates but marked unlisted
|
|
||||||
- **Timeline**: Only followers see it
|
|
||||||
- **Example**: Limited audience content
|
|
||||||
|
|
||||||
### Followers-Only Posts
|
|
||||||
- **What**: Posts only to followers, no federated boost
|
|
||||||
- **Federation**: ⚠️ May not federate fully
|
|
||||||
- **Timeline**: Only your followers
|
|
||||||
- **Example**: Very sensitive content
|
|
||||||
|
|
||||||
## Following Etiquette
|
|
||||||
|
|
||||||
### Best Practices
|
|
||||||
|
|
||||||
1. **Check before following**:
|
|
||||||
- Read their bio and recent posts
|
|
||||||
- Make sure they're who you think they are
|
|
||||||
- Check if their content aligns with your interests
|
|
||||||
|
|
||||||
2. **Start with interactions**:
|
|
||||||
- Like a few posts first
|
|
||||||
- Reply thoughtfully
|
|
||||||
- Share interesting content
|
|
||||||
- Then follow if you want to see more
|
|
||||||
|
|
||||||
3. **Respect instance culture**:
|
|
||||||
- Each instance has its own norms
|
|
||||||
- Read their community guidelines
|
|
||||||
- Be mindful of local rules
|
|
||||||
|
|
||||||
4. **Don't spam**:
|
|
||||||
- Don't mass-follow users
|
|
||||||
- Don't send unwanted DMs
|
|
||||||
- Don't repeatedly like old posts
|
|
||||||
|
|
||||||
5. **Use appropriate post visibility**:
|
|
||||||
- Public for general content
|
|
||||||
- Unlisted for updates to followers
|
|
||||||
- Private for sensitive topics
|
|
||||||
|
|
||||||
### Red Flags to Watch
|
|
||||||
|
|
||||||
1. **Suspicious accounts**:
|
|
||||||
- Newly created with generic content
|
|
||||||
- Only posting promotional links
|
|
||||||
- Unusual following patterns
|
|
||||||
|
|
||||||
2. **Instances with poor moderation**:
|
|
||||||
- Lots of spam in public timelines
|
|
||||||
- Harassment goes unaddressed
|
|
||||||
- You may want to block the instance
|
|
||||||
|
|
||||||
3. **Content warnings not respected**:
|
|
||||||
- Users posting unmarked sensitive content
|
|
||||||
- You can report/block these users
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### "Follow button doesn't work"
|
|
||||||
|
|
||||||
**Possible causes**:
|
|
||||||
1. User doesn't exist
|
|
||||||
2. Instance is down
|
|
||||||
3. Network connectivity issue
|
|
||||||
|
|
||||||
**What to do**:
|
|
||||||
1. Verify the username/domain is correct
|
|
||||||
2. Try searching for them again
|
|
||||||
3. Check your internet connection
|
|
||||||
4. Try again in a few minutes
|
|
||||||
|
|
||||||
### "User doesn't appear in Following list"
|
|
||||||
|
|
||||||
**Possible causes**:
|
|
||||||
1. Follow was rejected (locked account)
|
|
||||||
2. Follow is still pending
|
|
||||||
3. Error in federation
|
|
||||||
|
|
||||||
**What to do**:
|
|
||||||
1. Check if their account is locked
|
|
||||||
2. Wait a few minutes for acceptance
|
|
||||||
3. Check your ActivityPub logs
|
|
||||||
4. Try following again
|
|
||||||
|
|
||||||
### "Can't find a user via search"
|
|
||||||
|
|
||||||
**Possible causes**:
|
|
||||||
1. Username/domain is wrong
|
|
||||||
2. User's instance is blocking your instance
|
|
||||||
3. User's profile is not discoverable
|
|
||||||
|
|
||||||
**What to do**:
|
|
||||||
1. Double-check the spelling
|
|
||||||
2. Try their full URL: `https://instance.com/@username`
|
|
||||||
3. Check if they're from a blocked instance
|
|
||||||
4. Contact them directly for their handle
|
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
### Follow a Remote User
|
|
||||||
|
|
||||||
**Endpoint**: `POST /api/activitypub/follow`
|
|
||||||
|
|
||||||
**Request**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"targetActorUri": "https://mastodon.social/users/alice"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Success Response**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"message": "Follow request sent. Waiting for acceptance.",
|
|
||||||
"targetActorUri": "https://mastodon.social/users/alice"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Get Your Following
|
|
||||||
|
|
||||||
**Endpoint**: `GET /api/activitypub/following?limit=50`
|
|
||||||
|
|
||||||
**Response**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"users": [
|
|
||||||
{
|
|
||||||
"actorUri": "https://mastodon.social/users/alice",
|
|
||||||
"username": "alice",
|
|
||||||
"displayName": "Alice Smith",
|
|
||||||
"bio": "I love tech!",
|
|
||||||
"avatarUrl": "https://...",
|
|
||||||
"followedAt": "2024-01-15T10:30:00Z",
|
|
||||||
"isLocal": false,
|
|
||||||
"instanceDomain": "mastodon.social"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Get Your Followers
|
|
||||||
|
|
||||||
**Endpoint**: `GET /api/activitypub/followers?limit=50`
|
|
||||||
|
|
||||||
**Response**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"users": [
|
|
||||||
{
|
|
||||||
"actorUri": "https://pleroma.site/users/bob",
|
|
||||||
"username": "bob",
|
|
||||||
"displayName": "Bob Jones",
|
|
||||||
"bio": "Federated user following me",
|
|
||||||
"avatarUrl": "https://...",
|
|
||||||
"followedAt": "2024-01-10T14:20:00Z",
|
|
||||||
"isLocal": false,
|
|
||||||
"instanceDomain": "pleroma.site"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Search Users
|
|
||||||
|
|
||||||
**Endpoint**: `GET /api/activitypub/search?query=@alice@domain.com&limit=20`
|
|
||||||
|
|
||||||
**Response**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"users": [
|
|
||||||
{
|
|
||||||
"actorUri": "https://mastodon.social/users/alice",
|
|
||||||
"username": "alice",
|
|
||||||
"displayName": "Alice Smith",
|
|
||||||
"bio": "Tech enthusiast",
|
|
||||||
"avatarUrl": "https://...",
|
|
||||||
"isLocal": false,
|
|
||||||
"instanceDomain": "mastodon.social"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Real Examples
|
|
||||||
|
|
||||||
### Example 1: Following a Mastodon User
|
|
||||||
|
|
||||||
**What you do**:
|
|
||||||
1. Search for `@alice@mastodon.social`
|
|
||||||
2. Click on Alice's profile
|
|
||||||
3. Click "Follow" button
|
|
||||||
4. Wait 1-2 seconds
|
|
||||||
5. ✅ Alice appears in your "Following" list
|
|
||||||
6. ✅ Alice's public posts appear in your timeline
|
|
||||||
|
|
||||||
**What happens technically**:
|
|
||||||
- Solar Network sends Follow to Alice's Mastodon instance
|
|
||||||
- Alice's Mastodon auto-accepts (unless locked)
|
|
||||||
- Mastodon sends Accept back to Solar Network
|
|
||||||
- Relationship stored in both databases
|
|
||||||
- Alice's future posts federate to Solar Network
|
|
||||||
|
|
||||||
### Example 2: Following a Locked Account
|
|
||||||
|
|
||||||
**What you do**:
|
|
||||||
1. Search for `@private@pleroma.site`
|
|
||||||
2. Click "Follow" button
|
|
||||||
3. ✅ See "Following..." (pending)
|
|
||||||
4. Wait for user to approve
|
|
||||||
|
|
||||||
**What happens technically**:
|
|
||||||
- Solar Network sends Follow to private@pleroma.site
|
|
||||||
- Private user receives notification
|
|
||||||
- Private user manually approves the request
|
|
||||||
- Private user's instance sends Accept
|
|
||||||
- ✅ Now following!
|
|
||||||
|
|
||||||
### Example 3: Following a Bot Account
|
|
||||||
|
|
||||||
**What you do**:
|
|
||||||
1. Search for `@news@botsin.space`
|
|
||||||
2. Click "Follow" button
|
|
||||||
3. ✅ Immediately following (bots auto-accept)
|
|
||||||
|
|
||||||
**What happens technically**:
|
|
||||||
- Follow is auto-accepted
|
|
||||||
- News posts appear in your timeline
|
|
||||||
- Regular updates from the bot
|
|
||||||
|
|
||||||
## Key Differences from Traditional Social Media
|
|
||||||
|
|
||||||
| Aspect | Traditional Social | ActivityPub |
|
|
||||||
|---------|------------------|-------------|
|
|
||||||
| Central server | ❌ No | ✅ Yes (per instance) |
|
|
||||||
| Multiple platforms | ❌ No | ✅ Yes (Mastodon, Pleroma, etc.) |
|
|
||||||
| Data ownership | ❌ On their servers | ✅ On your server |
|
|
||||||
| Blocking | ❌ One platform | ✅ Per instance |
|
|
||||||
| Migration | ❌ Difficult | ✅ Use your own domain |
|
|
||||||
| Federation | ❌ No | ✅ Built-in |
|
|
||||||
|
|
||||||
## Getting Help
|
|
||||||
|
|
||||||
If you have issues following users:
|
|
||||||
|
|
||||||
1. **Check the main guide**: See `HOW_TO_FOLLOW_FEDIVERSE_USERS.md`
|
|
||||||
2. **Check your logs**: Look for ActivityPub errors
|
|
||||||
3. **Test the API**: Use curl to test follow endpoints directly
|
|
||||||
4. **Verify the user**: Make sure the user exists on their instance
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
Following fediverse users in Solar Network:
|
|
||||||
|
|
||||||
1. **Simple**: Just search and click "Follow"
|
|
||||||
2. **Works both ways**: You can follow them, they can follow you
|
|
||||||
3. **Works across instances**: Mastodon, Pleroma, Lemmy, etc.
|
|
||||||
4. **Federated content**: Their posts appear in your timeline
|
|
||||||
5. **Full interactions**: Like, reply, boost their posts
|
|
||||||
|
|
||||||
It works just like following on any other social platform, but with the added benefit of being able to follow users on completely different services! 🌍
|
|
||||||
@@ -1,406 +0,0 @@
|
|||||||
# How to Follow (Subscribe to) Fediverse Users in Solar Network
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
In ActivityPub terminology, "subscribing" to a user is called **"following"**. This guide explains how users in Solar Network can follow users from other federated services (Mastodon, Pleroma, etc.).
|
|
||||||
|
|
||||||
## User Guide: How to Follow Fediverse Users
|
|
||||||
|
|
||||||
### Method 1: Via Search (Recommended)
|
|
||||||
|
|
||||||
1. **Search for the user**:
|
|
||||||
- Type their full address in the search bar: `@username@domain.com`
|
|
||||||
- Example: `@alice@mastodon.social`
|
|
||||||
- Example: `@bob@pleroma.site`
|
|
||||||
|
|
||||||
2. **View their profile**:
|
|
||||||
- Click on the search result
|
|
||||||
- You'll see their profile, bio, and recent posts
|
|
||||||
|
|
||||||
3. **Click "Follow" button**:
|
|
||||||
- Solar Network sends a Follow activity to their instance
|
|
||||||
- The remote instance will send back an Accept
|
|
||||||
- The user now appears in your "Following" list
|
|
||||||
|
|
||||||
### Method 2: Via Profile URL
|
|
||||||
|
|
||||||
1. **Visit their profile directly**:
|
|
||||||
- If you know their profile URL, visit it directly
|
|
||||||
- Example: `https://mastodon.social/@alice`
|
|
||||||
|
|
||||||
2. **Look for "Follow" button**:
|
|
||||||
- Click it to follow
|
|
||||||
|
|
||||||
3. **Confirm the follow**:
|
|
||||||
- Solar Network will send the follow request
|
|
||||||
- Wait for acceptance (usually immediate)
|
|
||||||
|
|
||||||
## What Happens Behind the Scenes
|
|
||||||
|
|
||||||
### The Follow Flow
|
|
||||||
|
|
||||||
```
|
|
||||||
User clicks "Follow"
|
|
||||||
↓
|
|
||||||
Solar Network creates Follow Activity
|
|
||||||
↓
|
|
||||||
Solar Network signs with publisher's private key
|
|
||||||
↓
|
|
||||||
Solar Network sends to remote user's inbox
|
|
||||||
↓
|
|
||||||
Remote instance verifies signature
|
|
||||||
↓
|
|
||||||
Remote instance processes the Follow
|
|
||||||
↓
|
|
||||||
Remote instance sends Accept Activity back
|
|
||||||
↓
|
|
||||||
Solar Network receives and processes Accept
|
|
||||||
↓
|
|
||||||
Relationship is established!
|
|
||||||
```
|
|
||||||
|
|
||||||
### Timeline Integration
|
|
||||||
|
|
||||||
Once you're following a user:
|
|
||||||
- ✅ Their public posts appear in your "Home" timeline
|
|
||||||
- ✅ Their posts are federated to your followers
|
|
||||||
- ✅ Their likes, replies, and boosts are visible
|
|
||||||
- ✅ You can interact with their content
|
|
||||||
|
|
||||||
## Following Different Types of Accounts
|
|
||||||
|
|
||||||
### Individual Users
|
|
||||||
- **What**: Regular users like you
|
|
||||||
- **Example**: `@alice@mastodon.social`
|
|
||||||
- **Works**: ✅ Full support
|
|
||||||
|
|
||||||
### Organizational/Bot Accounts
|
|
||||||
- **What**: Groups, bots, or organizations
|
|
||||||
- **Example**: `@official@newsbot.site`
|
|
||||||
- **Works**: ✅ Full support
|
|
||||||
|
|
||||||
### Locked Accounts
|
|
||||||
- **What**: Users who manually approve followers
|
|
||||||
- **Example**: `@private@pleroma.site`
|
|
||||||
- **Works**: ✅ Follow request sent, waits for approval
|
|
||||||
|
|
||||||
## Managing Your Follows
|
|
||||||
|
|
||||||
### View Who You're Following
|
|
||||||
|
|
||||||
**API Endpoint**: `GET /api/activitypub/following`
|
|
||||||
|
|
||||||
**Response Example**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"users": [
|
|
||||||
{
|
|
||||||
"actorUri": "https://mastodon.social/users/alice",
|
|
||||||
"username": "alice",
|
|
||||||
"displayName": "Alice Smith",
|
|
||||||
"bio": "I love tech and coffee! ☕",
|
|
||||||
"avatarUrl": "https://cdn.mastodon.social/avatars/...",
|
|
||||||
"followedAt": "2024-01-15T10:30:00Z",
|
|
||||||
"isLocal": false,
|
|
||||||
"instanceDomain": "mastodon.social"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Unfollowing Someone
|
|
||||||
|
|
||||||
**API Endpoint**: `POST /api/activitypub/unfollow`
|
|
||||||
|
|
||||||
**Request Body**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"targetActorUri": "https://mastodon.social/users/alice"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"message": "Unfollowed successfully"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Searching Fediverse Users
|
|
||||||
|
|
||||||
**API Endpoint**: `GET /api/activitypub/search?query=@username@domain.com`
|
|
||||||
|
|
||||||
**Response Example**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"users": [
|
|
||||||
{
|
|
||||||
"actorUri": "https://mastodon.social/users/alice",
|
|
||||||
"username": "alice",
|
|
||||||
"displayName": "Alice Smith",
|
|
||||||
"bio": "Software developer | Mastodon user",
|
|
||||||
"avatarUrl": "https://cdn.mastodon.social/avatars/...",
|
|
||||||
"isLocal": false,
|
|
||||||
"instanceDomain": "mastodon.social"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Follow States
|
|
||||||
|
|
||||||
| State | Meaning | What User Sees |
|
|
||||||
|--------|---------|----------------|
|
|
||||||
| Pending | Follow request sent, waiting for response | "Following..." (loading) |
|
|
||||||
| Accepted | Remote user accepted | "Following" ✓ |
|
|
||||||
| Rejected | Remote user declined | "Follow" button available again |
|
|
||||||
| Failed | Error occurred | "Error following" message |
|
|
||||||
|
|
||||||
## Privacy & Visibility
|
|
||||||
|
|
||||||
### Public Posts
|
|
||||||
- ✅ Federate to your followers automatically
|
|
||||||
- ✅ Appear in remote instances' timelines
|
|
||||||
- ✅ Can be boosted/liked by remote users
|
|
||||||
|
|
||||||
### Private Posts
|
|
||||||
- ❌ Do not federate
|
|
||||||
- ❌ Only visible to your local followers
|
|
||||||
- ❌ Not sent to remote instances
|
|
||||||
|
|
||||||
### Unlisted Posts
|
|
||||||
- ⚠️ Federate but not in public timelines
|
|
||||||
- ⚠️ Only visible to followers
|
|
||||||
|
|
||||||
## Best Practices for Users
|
|
||||||
|
|
||||||
### When Following Someone
|
|
||||||
|
|
||||||
1. **Check their profile first**:
|
|
||||||
- Make sure they're who you think they are
|
|
||||||
- Read their bio to understand their content
|
|
||||||
|
|
||||||
2. **Start with a few interactions**:
|
|
||||||
- Like a few posts
|
|
||||||
- Reply to something interesting
|
|
||||||
- Don't overwhelm their timeline
|
|
||||||
|
|
||||||
3. **Respect their instance's rules**:
|
|
||||||
- Each instance has its own guidelines
|
|
||||||
- Read community rules before interacting
|
|
||||||
|
|
||||||
4. **Report spam/harassment**:
|
|
||||||
- Use instance blocking features
|
|
||||||
- Report to instance admins
|
|
||||||
|
|
||||||
### Following Across Instances
|
|
||||||
|
|
||||||
1. **Use their full address**:
|
|
||||||
- `@username@instance.com`
|
|
||||||
- This helps identify which instance they're on
|
|
||||||
|
|
||||||
2. **Be aware of instance culture**:
|
|
||||||
- Each instance has its own norms
|
|
||||||
- Some are more technical, others more casual
|
|
||||||
|
|
||||||
3. **Check if they're from your instance**:
|
|
||||||
- Local users show `isLocal: true`
|
|
||||||
- Usually faster interaction
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### "Follow button doesn't work"
|
|
||||||
|
|
||||||
**Possible Causes**:
|
|
||||||
1. User doesn't exist
|
|
||||||
2. Instance is down
|
|
||||||
3. Network issue
|
|
||||||
|
|
||||||
**Solutions**:
|
|
||||||
1. Verify the user's address is correct
|
|
||||||
2. Check if the instance is accessible
|
|
||||||
3. Check your internet connection
|
|
||||||
4. Try again in a few minutes
|
|
||||||
|
|
||||||
### "User doesn't appear in Following list"
|
|
||||||
|
|
||||||
**Possible Causes**:
|
|
||||||
1. Follow was rejected
|
|
||||||
2. Still waiting for acceptance (locked accounts)
|
|
||||||
3. Error in federation
|
|
||||||
|
|
||||||
**Solutions**:
|
|
||||||
1. Check the follow status via API
|
|
||||||
2. Try following again
|
|
||||||
3. Check if their account is locked
|
|
||||||
4. Contact support if issue persists
|
|
||||||
|
|
||||||
### "Can't find a user"
|
|
||||||
|
|
||||||
**Possible Causes**:
|
|
||||||
1. Wrong username or domain
|
|
||||||
2. User doesn't exist
|
|
||||||
3. Instance blocking your instance
|
|
||||||
|
|
||||||
**Solutions**:
|
|
||||||
1. Double-check the address
|
|
||||||
2. Try searching from a different instance
|
|
||||||
3. Contact the user directly for their handle
|
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
### Follow a Remote User
|
|
||||||
|
|
||||||
**Endpoint**: `POST /api/activitypub/follow`
|
|
||||||
|
|
||||||
**Request**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"targetActorUri": "https://mastodon.social/users/alice"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response**: `200 OK`
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"message": "Follow request sent. Waiting for acceptance.",
|
|
||||||
"targetActorUri": "https://mastodon.social/users/alice"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Get Following List
|
|
||||||
|
|
||||||
**Endpoint**: `GET /api/activitypub/following?limit=50`
|
|
||||||
|
|
||||||
**Response**: `200 OK`
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"users": [
|
|
||||||
{
|
|
||||||
"actorUri": "https://mastodon.social/users/alice",
|
|
||||||
"username": "alice",
|
|
||||||
"displayName": "Alice Smith",
|
|
||||||
"bio": "...",
|
|
||||||
"avatarUrl": "...",
|
|
||||||
"followedAt": "2024-01-15T10:30:00Z",
|
|
||||||
"isLocal": false,
|
|
||||||
"instanceDomain": "mastodon.social"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Get Followers List
|
|
||||||
|
|
||||||
**Endpoint**: `GET /api/activitypub/followers?limit=50`
|
|
||||||
|
|
||||||
**Response**: `200 OK`
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"users": [
|
|
||||||
{
|
|
||||||
"actorUri": "https://mastodon.social/users/alice",
|
|
||||||
"username": "alice",
|
|
||||||
"displayName": "Alice Smith",
|
|
||||||
"bio": "...",
|
|
||||||
"avatarUrl": "...",
|
|
||||||
"isLocal": false,
|
|
||||||
"instanceDomain": "mastodon.social"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Search Users
|
|
||||||
|
|
||||||
**Endpoint**: `GET /api/activitypub/search?query=alice&limit=20`
|
|
||||||
|
|
||||||
**Response**: `200 OK`
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"users": [
|
|
||||||
{
|
|
||||||
"actorUri": "https://mastodon.social/users/alice",
|
|
||||||
"username": "alice",
|
|
||||||
"displayName": "Alice Smith",
|
|
||||||
"bio": "...",
|
|
||||||
"avatarUrl": "...",
|
|
||||||
"isLocal": false,
|
|
||||||
"instanceDomain": "mastodon.social"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## What's Different About ActivityPub Following?
|
|
||||||
|
|
||||||
Unlike traditional social media:
|
|
||||||
|
|
||||||
| Feature | Traditional Social | ActivityPub |
|
|
||||||
|---------|------------------|-------------|
|
|
||||||
| Central server | ✅ Yes | ❌ No - federated |
|
|
||||||
| All users on same platform | ✅ Yes | ❌ No - multiple platforms |
|
|
||||||
| Blocked instances | ❌ No | ✅ Yes - instance blocking |
|
|
||||||
| Following across platforms | ❌ No | ✅ Yes - works with Mastodon, Pleroma, etc. |
|
|
||||||
| Your data stays on your server | ❌ Maybe | ✅ Yes - you control your data |
|
|
||||||
|
|
||||||
## User Experience Considerations
|
|
||||||
|
|
||||||
### Making It Easy
|
|
||||||
|
|
||||||
1. **Auto-discovery**:
|
|
||||||
- When users search for `@username`, suggest `@username@domain.com`
|
|
||||||
- Offer to search the fediverse
|
|
||||||
|
|
||||||
2. **Clear UI feedback**:
|
|
||||||
- Show "Follow request sent..."
|
|
||||||
- Show "They accepted!" notification
|
|
||||||
- Show "Follow request rejected" message
|
|
||||||
|
|
||||||
3. **Helpful tooltips**:
|
|
||||||
- Explain what ActivityPub is
|
|
||||||
- Show which instance a user is from
|
|
||||||
- Explain locked accounts
|
|
||||||
|
|
||||||
4. **Profile badges**:
|
|
||||||
- Show instance icon/logo
|
|
||||||
- Show if user is from same instance
|
|
||||||
- Show if user is verified
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Following a Mastodon User
|
|
||||||
|
|
||||||
**User searches**: `@alice@mastodon.social`
|
|
||||||
|
|
||||||
**What happens**:
|
|
||||||
1. Solar Network fetches Alice's actor profile
|
|
||||||
2. Solar Network stores Alice in `fediverse_actors`
|
|
||||||
3. Solar Network sends Follow to Alice's inbox
|
|
||||||
4. Alice's instance accepts
|
|
||||||
5. Solar Network stores relationship in `fediverse_relationships`
|
|
||||||
6. Alice's posts now appear in user's timeline
|
|
||||||
|
|
||||||
### Following a Local User
|
|
||||||
|
|
||||||
**User searches**: `@bob`
|
|
||||||
|
|
||||||
**What happens**:
|
|
||||||
1. Solar Network finds Bob's publisher
|
|
||||||
2. Relationship created locally (no federation needed)
|
|
||||||
3. Bob's posts appear in user's timeline immediately
|
|
||||||
4. Same as traditional social media following
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
Following fediverse users in Solar Network:
|
|
||||||
|
|
||||||
1. **Search by `@username@domain.com`** - Works for any ActivityPub instance
|
|
||||||
2. **Click "Follow"** - Sends federated follow request
|
|
||||||
3. **Wait for acceptance** - Remote user can approve or auto-accept
|
|
||||||
4. **See their posts in your timeline** - Content federates to you
|
|
||||||
5. **Interact normally** - Like, reply, boost, etc.
|
|
||||||
|
|
||||||
All of this is handled automatically by the ActivityPub implementation!
|
|
||||||
@@ -1,298 +0,0 @@
|
|||||||
# ActivityPub UI Implementation
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Complete UI implementation for ActivityPub features in Solian client, including search, following, and followers screens.
|
|
||||||
|
|
||||||
## Created Files
|
|
||||||
|
|
||||||
### 1. Widgets (`lib/widgets/activitypub/`)
|
|
||||||
|
|
||||||
#### `activitypub.dart`
|
|
||||||
- **Purpose**: Export file for ActivityPub widgets
|
|
||||||
- **Exports**: `ActivityPubUserListItem`
|
|
||||||
|
|
||||||
#### `user_list_item.dart`
|
|
||||||
- **Purpose**: Reusable list item widget for displaying ActivityPub users
|
|
||||||
- **Features**:
|
|
||||||
- Avatar with remote instance indicator (public icon)
|
|
||||||
- Display name with instance badge (e.g., "mastodon.social")
|
|
||||||
- Bio with truncation (max 2 lines)
|
|
||||||
- Followed at timestamp (relative time)
|
|
||||||
- Follow/Unfollow buttons with loading states
|
|
||||||
- Tap callback for navigation to profile
|
|
||||||
|
|
||||||
### 2. Screens (`lib/screens/activitypub/`)
|
|
||||||
|
|
||||||
#### `activitypub.dart`
|
|
||||||
- **Purpose**: Export file for ActivityPub screens
|
|
||||||
- **Exports**: `ActivityPubSearchScreen`, `ActivityPubListScreen`
|
|
||||||
|
|
||||||
#### `search.dart`
|
|
||||||
- **Purpose**: Search and follow ActivityPub users from other instances
|
|
||||||
- **Features**:
|
|
||||||
- Search bar with 500ms debounce
|
|
||||||
- Real-time search results
|
|
||||||
- Instant follow/unfollow actions
|
|
||||||
- Local tracking of followed users
|
|
||||||
- Empty states for no search and no results
|
|
||||||
- Refresh support via pull-to-refresh
|
|
||||||
- User feedback via snack bars
|
|
||||||
- **User Flow**:
|
|
||||||
1. User enters search query (e.g., `@alice@mastodon.social`)
|
|
||||||
2. Results appear after debounce
|
|
||||||
3. User taps "Follow" → Follow request sent
|
|
||||||
4. Success message shown
|
|
||||||
5. Button updates to "Unfollow"
|
|
||||||
|
|
||||||
#### `list.dart`
|
|
||||||
- **Purpose**: Display following/followers lists
|
|
||||||
- **Features**:
|
|
||||||
- Reusable for both Following and Followers
|
|
||||||
- Local state management
|
|
||||||
- Per-user loading states during actions
|
|
||||||
- Empty states with helpful hints
|
|
||||||
- Refresh support
|
|
||||||
- Auto-update lists when actions occur
|
|
||||||
- **Types**:
|
|
||||||
- `ActivityPubListType.following`: Shows users you follow
|
|
||||||
- `ActivityPubListType.followers`: Shows users who follow you
|
|
||||||
- **User Flow**:
|
|
||||||
1. User opens Following/Followers screen
|
|
||||||
2. List loads from API
|
|
||||||
3. User can unfollow (Following tab) or follow (Followers tab)
|
|
||||||
4. List updates automatically
|
|
||||||
5. Success/error messages shown
|
|
||||||
|
|
||||||
## Design Patterns
|
|
||||||
|
|
||||||
### Follows Project Conventions
|
|
||||||
|
|
||||||
1. **Material 3 Design**: All widgets use Material 3 components
|
|
||||||
2. **Styled Widget Package**: Used for `.padding()`, `.textColor()`, etc.
|
|
||||||
3. **Riverpod State Management**: Hooks for local state, providers for global state
|
|
||||||
4. **Error Handling**: `showErrorAlert()` from `alert.dart` for user feedback
|
|
||||||
5. **Success Feedback**: `showSnackBar()` for quick notifications
|
|
||||||
6. **Localization**: All strings use `.tr()` with placeholder args
|
|
||||||
|
|
||||||
### Color Scheme & Theming
|
|
||||||
|
|
||||||
- **Remote Badge**: Uses `Theme.colorScheme.primary` for indicator
|
|
||||||
- **Instance Tag**: Uses `Theme.colorScheme.secondaryContainer`
|
|
||||||
- **Text Colors**: Adaptive based on theme (dark/light)
|
|
||||||
- **States**: Loading indicators with standard `CircularProgressIndicator`
|
|
||||||
|
|
||||||
### Spacing & Layout
|
|
||||||
|
|
||||||
- **List Item Padding**: `EdgeInsets.only(left: 16, right: 12)`
|
|
||||||
- **Avatar Size**: 24px radius (48px diameter)
|
|
||||||
- **Badge Size**: Small (10px font) with 6px horizontal padding
|
|
||||||
- **Button Size**: Minimum 88px width, 36px height
|
|
||||||
|
|
||||||
## Translations Added
|
|
||||||
|
|
||||||
### New Keys in `assets/i18n/en-US.json`
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"searchFediverse": "Search Fediverse",
|
|
||||||
"searchFediverseHint": "Search by address, e.g. {}",
|
|
||||||
"searchFediverseEmpty": "Search for users on other ActivityPub instances",
|
|
||||||
"searchFediverseNoResults": "No users found for this search",
|
|
||||||
"following": "Following",
|
|
||||||
"followers": "Followers",
|
|
||||||
"follow": "Follow",
|
|
||||||
"unfollow": "Unfollow",
|
|
||||||
"followedUser": "Followed @{}",
|
|
||||||
"unfollowedUser": "Unfollowed @{}",
|
|
||||||
"followingEmpty": "You're not following anyone yet",
|
|
||||||
"followersEmpty": "No followers yet",
|
|
||||||
"followingEmptyHint": "Start by searching for users or explore other instances"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage Examples
|
|
||||||
|
|
||||||
### Using Search Screen
|
|
||||||
|
|
||||||
```dart
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
import 'package:island/screens/activitypub/activitypub.dart';
|
|
||||||
|
|
||||||
// In navigation or route
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => const ActivityPubSearchScreen(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Or using go_router
|
|
||||||
context.push('/activitypub/search');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using List Screen
|
|
||||||
|
|
||||||
```dart
|
|
||||||
// Following
|
|
||||||
ActivityPubListScreen(
|
|
||||||
type: ActivityPubListType.following,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Followers
|
|
||||||
ActivityPubListScreen(
|
|
||||||
type: ActivityPubListType.followers,
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using User List Item Widget
|
|
||||||
|
|
||||||
```dart
|
|
||||||
ActivityPubUserListItem(
|
|
||||||
user: user,
|
|
||||||
isFollowing: isFollowing,
|
|
||||||
isLoading: isLoading,
|
|
||||||
onFollow: () => handleFollow(user),
|
|
||||||
onUnfollow: () => handleUnfollow(user),
|
|
||||||
onTap: () => navigateToProfile(user),
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Integration Points
|
|
||||||
|
|
||||||
### Navigation Integration
|
|
||||||
|
|
||||||
To add ActivityPub screens to navigation:
|
|
||||||
|
|
||||||
1. **Option A**: Add to existing tab/navigation structure
|
|
||||||
2. **Option B**: Add as standalone routes in `go_router`
|
|
||||||
3. **Option C**: Add to profile menu overflow menu
|
|
||||||
|
|
||||||
### Service Integration
|
|
||||||
|
|
||||||
All screens use `activityPubServiceProvider`:
|
|
||||||
|
|
||||||
```dart
|
|
||||||
import 'package:island/services/activitypub_service.dart';
|
|
||||||
|
|
||||||
final service = ref.read(activityPubServiceProvider);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Error Handling
|
|
||||||
|
|
||||||
All errors are caught and displayed using:
|
|
||||||
|
|
||||||
```dart
|
|
||||||
try {
|
|
||||||
// API call
|
|
||||||
} catch (err) {
|
|
||||||
showErrorAlert(err);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing Checklist
|
|
||||||
|
|
||||||
- [ ] Search for existing Mastodon user
|
|
||||||
- [ ] Search for Pleroma user
|
|
||||||
- [ ] Follow a user
|
|
||||||
- [ ] Unfollow a user
|
|
||||||
- [ ] View following list
|
|
||||||
- [ ] View followers list
|
|
||||||
- [ ] Test empty states
|
|
||||||
- [ ] Test loading states
|
|
||||||
- [ ] Test error handling
|
|
||||||
- [ ] Test dark mode
|
|
||||||
- [ ] Test RTL languages (if supported)
|
|
||||||
|
|
||||||
## Technical Details
|
|
||||||
|
|
||||||
### Dependencies
|
|
||||||
|
|
||||||
**Already in project**:
|
|
||||||
- ✅ `cached_network_image` - For avatar images
|
|
||||||
- ✅ `easy_localization` - For translations
|
|
||||||
- ✅ `hooks_riverpod` - For state management
|
|
||||||
- ✅ `flutter_hooks` - For hooks (useState, useEffect, etc.)
|
|
||||||
- ✅ `material_symbols_icons` - For icons
|
|
||||||
- ✅ `relative_time` - For timestamp formatting
|
|
||||||
- ✅ `island/services/activitypub_service.dart` - API service (created earlier)
|
|
||||||
- ✅ `island/widgets/alert.dart` - Error/success dialogs
|
|
||||||
- ✅ `island/models/activitypub.dart` - Data models (created earlier)
|
|
||||||
|
|
||||||
### Performance Considerations
|
|
||||||
|
|
||||||
1. **Debounced Search**: 500ms delay prevents excessive API calls
|
|
||||||
2. **Local State Tracking**: `followingUris` Set prevents duplicate API calls
|
|
||||||
3. **Conditional Rebuilds**: Widget only rebuilds when necessary
|
|
||||||
4. **Image Caching**: Uses `CachedNetworkImageProvider` for avatars
|
|
||||||
|
|
||||||
### Accessibility
|
|
||||||
|
|
||||||
1. **Semantic Labels**: All ListTile widgets have proper content
|
|
||||||
2. **Touch Targets**: Minimum 44px touch targets for buttons
|
|
||||||
3. **Color Contrast**: Follows Material 3 color guidelines
|
|
||||||
4. **Loading Indicators**: Visual feedback during async operations
|
|
||||||
|
|
||||||
## Future Enhancements
|
|
||||||
|
|
||||||
### Potential Additions
|
|
||||||
|
|
||||||
1. **Profile Integration**: Show ActivityPub profile details
|
|
||||||
2. **Post Timeline**: Show federated posts from followed users
|
|
||||||
3. **Instance Blocking**: Block entire ActivityPub instances
|
|
||||||
4. **Advanced Search**: Filter by instance, user type, etc.
|
|
||||||
5. **Batch Actions**: Follow/unfollow multiple users at once
|
|
||||||
6. **Suggested Users**: Show recommended users to follow
|
|
||||||
7. **Recent Activity**: Show recent interactions
|
|
||||||
8. **Notifications**: Follow/unfollow notifications
|
|
||||||
|
|
||||||
### Localization
|
|
||||||
|
|
||||||
Need to add same keys to other language files:
|
|
||||||
- `es-ES.json`
|
|
||||||
- `ja-JP.json`
|
|
||||||
- `ko-KR.json`
|
|
||||||
- etc.
|
|
||||||
|
|
||||||
## Browser Testing
|
|
||||||
|
|
||||||
Test with real ActivityPub instances:
|
|
||||||
- mastodon.social
|
|
||||||
- pixelfed.social
|
|
||||||
- lemmy.world
|
|
||||||
- pleroma.site
|
|
||||||
- fosstodon.org
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Common Issues
|
|
||||||
|
|
||||||
1. **Search returns no results**
|
|
||||||
- Check if user exists on remote instance
|
|
||||||
- Verify instance is accessible
|
|
||||||
- Try full URL instead of handle
|
|
||||||
|
|
||||||
2. **Follow button not working**
|
|
||||||
- Check if user is already following
|
|
||||||
- Verify server is online
|
|
||||||
- Check API logs
|
|
||||||
|
|
||||||
3. **Avatar not loading**
|
|
||||||
- Check remote avatar URL
|
|
||||||
- Verify network connection
|
|
||||||
- Check image cache
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
✅ **Fully functional ActivityPub UI** with:
|
|
||||||
- Search screen for discovering fediverse users
|
|
||||||
- Following/Followers list screens
|
|
||||||
- Reusable user list item component
|
|
||||||
- Proper error handling and user feedback
|
|
||||||
- Material 3 design
|
|
||||||
- Responsive layout
|
|
||||||
- Local state management
|
|
||||||
- Debounced search
|
|
||||||
- Empty states and loading indicators
|
|
||||||
|
|
||||||
**Ready for integration into main app navigation!** 🎉
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import 'package:island/screens/activitypub/activitypub.dart';
|
|
||||||
|
|
||||||
Add to Explore tab routes, after postCategoryDetail route:
|
|
||||||
|
|
||||||
GoRoute(
|
|
||||||
name: 'activitypubSearch',
|
|
||||||
path: '/activitypub/search',
|
|
||||||
builder: (context, state) => const ActivityPubSearchScreen(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'activitypubFollowing',
|
|
||||||
path: '/activitypub/following',
|
|
||||||
builder: (context, state) => const ActivityPubListScreen(
|
|
||||||
type: ActivityPubListType.following,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'activitypubFollowers',
|
|
||||||
path: '/activitypub/followers',
|
|
||||||
builder: (context, state) => const ActivityPubListScreen(
|
|
||||||
type: ActivityPubListType.followers,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Reference in New Issue
Block a user