wiki: add Migration Guide page

2026-05-29 02:15:32 +00:00
parent be47ce61b2
commit fc60217dfd

196
Migration-Guide.md Normal file

@@ -0,0 +1,196 @@
# Migration Guide — 0.1.x → 0.3.0
Work through each step top-to-bottom. Each step is independent after the previous one compiles.
---
## Step 1: Update `Cargo.toml`
```toml
# Before
k-ap = { git = "...", tag = "v0.1.10" }
# After (registry)
k-ap = { version = "0.3.0", registry = "gitea" }
# After (git fallback)
k-ap = { git = "https://git.gabrielkaszewski.dev/GKaszewski/k-ap.git", tag = "v0.3.0" }
```
---
## Step 2: Rename `backfill_outbox` → `import_remote_outbox`
```rust
// Before
service.backfill_outbox(outbox_url, actor_url).await?;
// After
service.import_remote_outbox(outbox_url, actor_url).await?;
```
---
## Step 3: Split `FederationRepository` into four traits
In 0.1.x one `FederationRepository` trait had ~34 methods. In 0.3.0 it is split into four:
| Trait | Methods |
|-------|---------|
| `ActivityRepository` | `is_activity_processed`, `mark_activity_processed` |
| `FollowRepository` | 20 methods — follower/following graph + migration |
| `ActorRepository` | 7 methods — keypairs, cache, announce tracking |
| `BlocklistRepository` | 8 methods — domain and actor blocklists |
Implement each on your existing struct. You will need to add these **new required methods** that did not exist in 0.1.x:
**`FollowRepository` — new:**
```rust
async fn count_accepted_followers(&self, local_user_id: Uuid) -> Result<usize>;
async fn get_accepted_followers_page(&self, local_user_id: Uuid, offset: u32, limit: usize) -> Result<Vec<RemoteActor>>;
async fn get_following_outbox_url(&self, local_user_id: Uuid, remote_actor_url: &str) -> Result<Option<String>>;
```
**`ActorRepository` — new:**
```rust
async fn remove_announce(&self, activity_id: &str, actor_url: &str) -> Result<()>;
async fn count_announces(&self, object_url: &str) -> Result<usize>;
```
---
## Step 4: Split `ApObjectHandler` into `ApContentReader` + `ApObjectHandler`
```rust
// Before — read methods on ApObjectHandler
async fn get_local_objects_page(&self, user_id: Uuid, cursor: Option<String>, limit: u64) -> ...;
async fn count_local_posts(&self, user_id: Uuid) -> ...;
// After — split into a separate trait
impl ApContentReader for MyDb {
async fn get_local_objects_page(
&self,
user_id: Uuid,
before: Option<DateTime<Utc>>, // cursor changed to before-timestamp
limit: usize,
) -> anyhow::Result<Vec<(Url, serde_json::Value, DateTime<Utc>)>> {
// return (ap_id, object_json, published_at) tuples, newest-first
}
async fn count_local_posts(&self) -> anyhow::Result<u64> {
// NOTE: no user_id parameter — counts ALL local posts for NodeInfo
}
// get_featured_objects has a default empty impl — override for pinned posts
}
```
`get_local_objects_for_user` (if present) was removed as dead code.
---
## Step 5: Update `ApObjectHandler` method signatures
Several callbacks changed signatures:
```rust
// Before
async fn on_create(&self, user_id: Uuid, object: Value) -> ...;
async fn on_update(&self, user_id: Uuid, object: Value) -> ...;
async fn on_delete(&self, user_id: Uuid, object_id: Url) -> ...;
async fn on_like(&self, user_id: Uuid, actor_url: Url, object_id: Url) -> ...;
async fn on_unlike(&self, user_id: Uuid, actor_url: Url, object_id: Url) -> ...;
async fn on_announce(&self, user_id: Uuid, actor_url: Url, object_id: Url) -> ...;
async fn on_mention(&self, user_id: Uuid, actor_url: Url, object: Value) -> ...;
// After
async fn on_create(&self, ap_id: &Url, actor_url: &Url, object: Value) -> ...;
async fn on_update(&self, ap_id: &Url, actor_url: &Url, object: Value) -> ...;
async fn on_delete(&self, ap_id: &Url, actor_url: &Url) -> ...;
async fn on_like(&self, object_url: &Url, actor_url: &Url) -> ...;
async fn on_unlike(&self, object_url: &Url, actor_url: &Url) -> ...;
async fn on_announce_received(&self, object_url: &Url, actor_url: &Url) -> ...; // renamed
async fn on_mention(&self, object_ap_id: &Url, mentioned_user_id: Uuid, actor_url: &Url) -> ...;
```
**New required methods** (no defaults):
```rust
async fn on_actor_removed(&self, actor_url: &Url) -> anyhow::Result<()>;
async fn on_announce_of_remote(&self, object_url: &Url, actor_url: &Url) -> anyhow::Result<()>;
```
**New optional method** (default no-op):
```rust
async fn on_announce_removed(&self, object_url: &Url, actor_url: &Url) -> anyhow::Result<()>;
```
---
## Step 6: Update `broadcast_create_note` and `broadcast_update_note`
```rust
// Before
service.broadcast_create_note(user_id, note_json, ApVisibility::Public).await?;
// After — pass inboxes of mentioned non-followers, or vec![] if none
service.broadcast_create_note(user_id, note_json, ApVisibility::Public, mentioned_inboxes).await?;
```
`ApVisibility` is a new enum. Replace any visibility handling:
- Public post → `ApVisibility::Public`
- Followers only → `ApVisibility::FollowersOnly`
- No delivery → `ApVisibility::Private`
---
## Step 7: Update `also_known_as` fields
```rust
// Before — Option<String>
ApUser { also_known_as: Some("https://old.example/users/alice".to_string()), .. }
// After — Vec<String>
ApUser { also_known_as: vec!["https://old.example/users/alice".to_string()], .. }
```
Same change for `LookedUpActor::also_known_as`.
---
## Step 8: Update the builder
```rust
// Before (positional arguments)
ActivityPubService::builder(repo, user_repo, handler, "https://example.com")
// After (named setters)
ActivityPubService::builder("https://example.com")
.activity_repo(db.clone())
.follow_repo(db.clone())
.actor_repo(db.clone())
.blocklist_repo(db.clone())
.user_repo(db.clone())
.content_reader(db.clone())
.object_handler(db.clone())
.build()
.await?
```
---
## Step 9: Add content negotiation routes
In 0.3.0, `GET /users/{id}`, `/followers`, and `/following` are no longer registered by `service.router()`.
Add these routes to your axum router and implement the content negotiation pattern. See [Routes Reference](Routes-Reference) for the full example with `service.actor_json()`, `service.followers_collection_json()`, and `service.following_collection_json()`.
---
## New opt-in features (no migration required)
These are new in 0.3.0 — no code breaks if you don't use them:
- `ApUser::discoverable: bool` — was hard-coded `true`; now configurable per user
- `ApUser::actor_type: ApActorType` — was hard-coded `Person`; now configurable
- `ApUser::featured_url: Option<Url>` — expose pinned posts; see [Pinned Posts](Pinned-Posts)
- `service.block_actor` / `service.unblock_actor` — block management from the service