Align ActivityPub key IDs with actor document #208
Labels
No labels
activitypub
admin
api
architecture
articles
auth
bug
cleanup
cli
comments
compatibility
config
contacts
content
database
deployment
design
dev-env
diagnostics
docs
documentation
email
enhancement
feature
federation
feed
health
homepage
implementation
integration
logging
mastodon
media
navigation
npm-packages
openapi
opengraph
packages
passkeys
preview
priority:high
priority:low
priority:medium
production
proof
proxy
public-routes
public-ui
release
safety
slug-api
slug-auth
slug-core
slug-federation
slug-media
slug-template
social
sources
status:active
status:canceled
status:done
status:inprogress
status:waiting
syndication
tailwind
template
test
web
webauthn
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
erik/slugkit#208
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Generated Slugkit ActivityPub signing and actor-key discovery must use a key URI that Mastodon can verify. A slugkit.com production experiment signed outbound
Acceptactivities with a standalone key URL (https://slugkit.com/users/slug/main-key), but the actor document still advertisedpublicKey.idashttps://slugkit.com/users/slug#main-key. Mastodon rejected the signed request with:Implement this correctly in the Slugkit template so generated sites either keep using the fragment key ID consistently, or switch to a standalone key endpoint and advertise that exact key ID from the actor document.
Background from slugkit.com
This came from slugkit.com task
task-becaf72bwhile debugging Mastodon follow acceptance for@slug@slugkit.com.Important slugkit.com branch/commits:
/home/erik/Private/code/forgejo/slugkit.comtask-becaf72b-activitypub-follow7bfb5d6 Serve actor keys for generic fetchesGET https://slugkit.com/users/slugreturn ActivityPub JSON even without an ActivityPubAcceptheader....#main-keyby requesting the actor URL without preserving the fragment.b0fd7a6 Sign follow accepts with standalone key URLGET /users/:username/main-keyendpoint returning aCryptographicKey.Acceptdelivery withhttps://slugkit.com/users/slug/main-key.src/federation/routes.tssrc/federation/follow.tssrc/federation/__tests__/routes.test.tsevcraddock/slugkit-com:0.1.6reached Mastodon, but Mastodon rejected it because the actor document did not advertise the standalone key ID.833f450 Revert "Sign follow accepts with standalone key URL"Current slugkit.com has been reverted to the previous
#main-keysigning behavior while retaining the generic actor fetch fix.Mastodon behavior to account for
Mastodon
ActivityPub::FetchRemoteKeyServicevalidates standalone legacy key ownership like this:keyId,owner,publicKeylist/object to include the exact key object'sid.For a legacy
CryptographicKey, Mastodon effectively requires:and the actor at
https://example.com/users/alicemust advertise that same exact key ID, for example:If the signer uses
/main-keybut the actor advertises#main-key, Mastodon rejects withpublicKey id ... does not correspond ....Mastodon actor refresh is not immediate:
Account::STALE_THRESHOLD = 1.day; there is also a 5-minute stoplight cool-off for repeated fetch/connect failures. Do not depend on cache expiry for correctness.Steps to reproduce
publicKey.id.Acceptback to Mastodon.Expected behavior
keyId./users/:username/main-key, that key object hasownerset to the actor URI and the actor'spublicKey.idmatches the standalone key URI./users/:username#main-key, actor fetches without an ActivityPubAcceptheader still return the ActivityPub actor JSON so Mastodon can resolve the key.Acceptdelivery for follows, and the remote UI transitions from pending follow (Cancel request) to active following (Unfollow) after reload.Actual behavior
#main-key.Acceptdelivery because the signaturekeyIddid not correspond to the actor's advertisedpublicKey.id.Implementation notes
src/federation/routes.tssrc/federation/follow.tssrc/federation/__tests__/routes.test.ts#main-keyeverywhere and ensure generic actor fetch works for key resolution, or/main-keyeverywhere: actor documentpublicKey.id, key endpointid, and HTTP SignaturekeyId./main-key, the actor document must include/main-key, not#main-key.Acceptheader if the template relies on fragment key IDs.Acceptance criteria
/users/:username/main-keyendpoint is generated, it returns aCryptographicKeywhoseidmatches the actor document'spublicKey.idand whoseowneris the actor URI.GET /users/:usernamekey-resolution fetches return ActivityPub actor JSON without requiring a specialAcceptheader.Acceptsigning using the advertised key URI.Public key not foundorpublicKey id ... does not correspond ....Dependencies
task-677d2a8f(Start generated ActivityPub queue worker).Synced from todu comment by @todu on 2026-06-27T21:55:36.661Z
Review update
Acceptance criteria: 6/6 met. Blocking issues: 0. Warnings: 0.
Synced from todu comment by @todu on 2026-06-27T23:42:11.537Z
Close gate
Readiness: READY
Evidence:
mainas merge commit8c40de4, with implementation commit9440874(fix: align ActivityPub sender key IDs).template/site/src/federation/routes.tsto return ActivityPub JSON for generic/users/*GET/HEAD key-resolution fetches.template/site/src/federation/__tests__/routes.test.tsverifies the actor advertises#multikey-1, generic actor fetch returns ActivityPub JSON including#main-keyand#multikey-1, and outbound followAcceptsigning uses the advertised key URI instead of#main-key.template/site/src/federation/README.mdrecords the chosen key-ID convention and Mastodon mismatch behavior.Acceptance criteria: 6/6 met.