A lightweight, zero-dependency Go library implementing the ActivityPub protocol (ActivityStreams 2.0). Designed for building federated social applications — Mastodon-compatible servers, bots, clients, and tools.
GAP stands for Go ActivityPub.
The name also happens to reflect a small personal coincidence at the time of creation.
- ActivityStreams 2.0 types — Actor, Note, Activity, Collection, WebFinger, NodeInfo
- HTTP Signatures — draft-cavage-http-signatures (RSA-SHA256 sign & verify)
- LD Signatures — RsaSignature2017 for backward compatibility
- Safe HTTP client — SSRF protection (blocks private/internal IPs)
- Activity delivery — signed POST to remote inboxes
- Key generation — RSA-2048 key pair generation
go get github.com/Guaderxx/gapactor := gap.Actor{
Context: []string{
gap.ContextActivityStreams,
gap.ContextSecurity,
},
ID: "https://example.com/users/alice",
Type: gap.ActorPerson, // use constants for type safety
PreferredUsername: "alice",
Name: "Alice",
Inbox: "https://example.com/users/alice/inbox",
Outbox: "https://example.com/users/alice/outbox",
Followers: "https://example.com/users/alice/followers",
Following: "https://example.com/users/alice/following",
PublicKey: &gap.PublicKey{
ID: "https://example.com/users/alice#main-key",
Owner: "https://example.com/users/alice",
PublicKeyPem: publicKeyPEM,
},
}// Create a Follow activity
follow := gap.Activity{
Context: []string{gap.ContextActivityStreams},
Type: gap.ActivityFollow, // use constants for type safety
Actor: "https://example.com/users/alice",
Object: "https://mastodon.example/users/bob",
}
body, _ := json.Marshal(follow)
// Deliver it to Bob's inbox
err := gap.DeliverActivity(
"https://mastodon.example/users/bob/inbox",
body,
"https://example.com/users/alice#main-key",
privateKeyPEM,
)client := gap.NewSafeClient()
// This will be blocked (private IP)
resp, err := client.Get("http://10.0.0.1/api")
// err: gap: connection blocked by SSRF protection
// This works normally
resp, err := client.Get("https://mastodon.social/.well-known/nodeinfo")resp := gap.WebFingerResponse{
Subject: "acct:alice@example.com",
Links: []gap.WebFingerLink{
{
Rel: "self",
Type: "application/activity+json",
Href: "https://example.com/users/alice",
},
},
}| Package | Contents |
|---|---|
gap |
All types and functions in a single flat package |
| Type | Description |
|---|---|
Actor |
ActivityPub actor (Person, Application, Service) |
Activity |
ActivityPub activity (Create, Follow, Like, etc.) |
Note |
ActivityPub note (equivalent to a Mastodon status) |
Collection |
Ordered collection (followers, following, outbox) |
PublicKey |
Actor's public key for HTTP Signatures |
WebFingerResponse |
RFC 7033 WebFinger JRD response |
NodeInfo |
NodeInfo 2.0 instance metadata |
Image |
Attached image in ActivityPub documents |
LDKeyPair |
RSA key pair for Linked Data Signatures |
Context URIs:
| Constant | Value |
|---|---|
ContextActivityStreams |
https://www.w3.org/ns/activitystreams |
ContextSecurity |
https://w3id.org/security/v1 |
PublicAddress |
https://www.w3.org/ns/activitystreams#Public |
Activity Types (gap.ActivityXxx):
| Constant | Value | Mastodon equivalent |
|---|---|---|
ActivityAccept |
Accept |
Accept follow |
ActivityAdd |
Add |
Add to collection |
ActivityAnnounce |
Announce |
Boost / reblog |
ActivityBlock |
Block |
Block user |
ActivityCreate |
Create |
Post a status |
ActivityDelete |
Delete |
Delete a status |
ActivityFlag |
Flag |
Report |
ActivityFollow |
Follow |
Follow user |
ActivityLike |
Like |
Favourite |
ActivityMove |
Move |
Account migration |
ActivityReject |
Reject |
Reject follow |
ActivityRemove |
Remove |
Remove from collection |
ActivityUndo |
Undo |
Undo an action |
ActivityUpdate |
Update |
Edit a status/profile |
ActivityView |
View |
View |
Actor Types (gap.ActorXxx):
| Constant | Value |
|---|---|
ActorPerson |
Person |
ActorApplication |
Application |
ActorService |
Service |
ActorGroup |
Group |
ActorOrganization |
Organization |
Object Types (gap.ObjectXxx):
| Constant | Value |
|---|---|
ObjectNote |
Note |
ObjectArticle |
Article |
ObjectDocument |
Document |
ObjectImage |
Image |
ObjectVideo |
Video |
ObjectAudio |
Audio |
ObjectPage |
Page |
ObjectEvent |
Event |
ObjectPlace |
Place |
ObjectQuestion |
Question |
ObjectCollectionType |
OrderedCollection |
| Function | Description |
|---|---|
SignRequest(req, keyID, privateKeyPEM) |
Sign an HTTP request with RSA-SHA256 |
VerifyRequest(req, publicKeyPEM) |
Verify an incoming HTTP signature (returns bool) |
AddDateHeader(req) |
Set the Date header for signing |
AddDigestHeader(req, body) |
Set the Digest header with SHA-256 |
DeliverActivity(inbox, body, keyID, privateKeyPEM) |
Sign & POST an activity to a remote inbox |
GenerateKeyPair() |
Generate a RSA-2048 key pair (returns PEM strings) |
| Function | Description |
|---|---|
SignJSONLD(doc, keyPair) |
Sign a JSON-LD document with RsaSignature2017 |
VerifyJSONLD(doc, sig, publicKeyPEM) |
Verify a Linked Data Signature |
| Function/Type | Description |
|---|---|
NewSafeClient() |
Create an HTTP client with SSRF protection |
SafeClient.Get(url) |
Safe GET request |
SafeClient.Post(url, contentType, body) |
Safe POST request |
ResolveActorURI(uri) |
Extract username & domain from an actor URI |
your-app/
├── handlers/ # Gin/Echo/Chi HTTP handlers
├── federation/ # ActivityPub server logic
├── storage/ # Database layer (your choice)
└── ...
└── imports github.com/Guaderxx/gap # Protocol types & crypto
Gap is framework-agnostic. It provides:
- Types — use them in your JSON serialization
- Crypto — sign and verify requests
- Client — make safe HTTP calls to remote servers
It does NOT provide:
- Database storage
- HTTP routing
- Queue/worker systems
- Real-time streaming
These belong in your application layer — gap is the protocol foundation.
See example/main.go for a complete working example of a minimal ActivityPub server.
Zero external dependencies. Gap uses only the Go standard library:
crypto/*— RSA, SHA-256, ECDH, AES-GCMnet/http— HTTP client with custom transportencoding/json,encoding/pem,encoding/base64
MIT. See LICENSE.