activitypub

package
v1.1.11 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 16, 2026 License: AGPL-3.0 Imports: 12 Imported by: 0

Documentation

Overview

Package activitypub provides parsing and validation utilities for ActivityPub protocol messages.

Index

Constants

View Source
const (
	// Actor types
	PersonType       = "Person"
	ServiceType      = "Service"
	GroupType        = "Group"
	OrganizationType = "Organization"
	ApplicationType  = "Application"

	// Activity types
	CreateType   = "Create"
	UpdateType   = "Update"
	DeleteType   = "Delete"
	FollowType   = "Follow"
	AcceptType   = "Accept"
	RejectType   = "Reject"
	LikeType     = "Like"
	AnnounceType = "Announce"
	UndoType     = "Undo"
	BlockType    = "Block"
	FlagType     = "Flag"
	MoveType     = "Move"
	AddType      = "Add"
	RemoveType   = "Remove"

	// Object types
	NoteType                  = "Note"
	ArticleType               = "Article"
	ImageType                 = "Image"
	VideoType                 = "Video"
	DocumentType              = "Document"
	CollectionType            = "Collection"
	OrderedCollectionType     = "OrderedCollection"
	OrderedCollectionPageType = "OrderedCollectionPage"
	TombstoneType             = "Tombstone"

	// Collection types
	InboxCollection     = "inbox"
	OutboxCollection    = "outbox"
	FollowersCollection = "followers"
	FollowingCollection = "following"
	LikedCollection     = "liked"

	// Public addressing
	PublicAddress = "https://www.w3.org/ns/activitystreams#Public"
)

Constants for common ActivityPub types

View Source
const (
	// HTTPScheme represents the HTTP URL scheme
	HTTPScheme = "http"
	// HTTPSScheme represents the HTTPS URL scheme
	HTTPSScheme = "https"
)

Variables

View Source
var (
	// ErrInvalidJSON is returned when JSON validation fails before parsing
	ErrInvalidJSON = apperrors.JSONFormatInvalid("invalid ActivityPub JSON")

	// ErrParseActivity is returned when activity parsing fails
	ErrParseActivity = apperrors.ActivityParsingFailed("activity", errors.New("activity parsing failed"))

	// ErrParseActor is returned when actor parsing fails
	ErrParseActor = apperrors.ActivityParsingFailed("actor", errors.New("actor parsing failed"))

	// ErrParseNote is returned when note parsing fails
	ErrParseNote = apperrors.ActivityParsingFailed("note", errors.New("note parsing failed"))

	// ErrInvalidActivity is returned when activity validation fails
	ErrInvalidActivity = apperrors.InvalidActivityObject()

	// ErrInvalidActor is returned when actor validation fails
	ErrInvalidActor = apperrors.NewValidationError("actor", "Invalid actor")

	// ErrInvalidNote is returned when note validation fails
	ErrInvalidNote = apperrors.NewValidationError("note", "Invalid note")

	// ErrEmptyRecipient is returned when a recipient field is empty
	ErrEmptyRecipient = apperrors.RequiredFieldMissing("recipient")

	// ErrInvalidRecipientURL is returned when a recipient URL is malformed
	ErrInvalidRecipientURL = apperrors.InvalidFormat("recipient_url", "valid URL format")

	// ErrInvalidURLScheme is returned when a recipient URL doesn't use HTTP(S)
	ErrInvalidURLScheme = apperrors.URLSchemeNotAllowed("", "")

	// ErrInvalidRecipientFormat is returned when a recipient URL format is invalid
	ErrInvalidRecipientFormat = apperrors.InvalidFormat("recipient_url", "valid URL format")

	// ErrDirectMessagePublicAddressing is returned when direct messages have public addressing
	ErrDirectMessagePublicAddressing = apperrors.BusinessRuleViolated("direct message addressing", map[string]interface{}{"rule": "no public addressing"})

	// ErrBCCInVisibleFields is returned when BCC recipients appear in visible addressing fields
	ErrBCCInVisibleFields = apperrors.BusinessRuleViolated("BCC privacy", map[string]interface{}{"rule": "BCC not in visible fields"})

	// Validation errors
	// ErrEmptyDomain is returned when domain is empty
	ErrEmptyDomain = apperrors.RequiredFieldMissing("domain")

	// ErrIPAddressAsDomain is returned when an IP address is used as a domain
	ErrIPAddressAsDomain = apperrors.NewValidationError("domain", "IP addresses cannot be used as domains")

	// ErrInvalidDomainFormat is returned when domain format is invalid
	ErrInvalidDomainFormat = apperrors.InvalidFormat("domain", "valid domain format")

	// ErrConsecutiveDots is returned when domain contains consecutive dots
	ErrConsecutiveDots = apperrors.NewValidationError("domain", "Domain cannot contain consecutive dots")

	// ErrInvalidActorIDURL is returned when actor ID URL is malformed
	ErrInvalidActorIDURL = apperrors.InvalidFormat("actor_id_url", "valid actor ID URL")

	// ErrActorIDScheme is returned when actor ID doesn't use HTTP(S)
	ErrActorIDScheme = apperrors.URLSchemeNotAllowed("", "")

	// ErrActorIDMissingPath is returned when actor ID has no path
	ErrActorIDMissingPath = apperrors.NewValidationError("actor_id", "Actor ID must have a path")

	// ErrInvalidWebfingerFormat is returned when webfinger format is invalid
	ErrInvalidWebfingerFormat = apperrors.InvalidFormat("webfinger", "acct:user@domain")

	// ErrInvalidDomainInActorID is returned when actor ID contains invalid domain
	ErrInvalidDomainInActorID = apperrors.NewValidationError("actor_id", "Invalid domain in actor ID")

	// ErrInvalidUsernameInWebfinger is returned when webfinger contains invalid username
	ErrInvalidUsernameInWebfinger = apperrors.NewValidationError("webfinger", "Invalid username")

	// ErrInvalidDomainInWebfinger is returned when webfinger contains invalid domain
	ErrInvalidDomainInWebfinger = apperrors.NewValidationError("webfinger", "Invalid domain")

	// ErrInvalidActivityType is returned when activity type is not recognized
	ErrInvalidActivityType = apperrors.NewValidationError("activity_type", "Invalid activity type")
)

Legacy error variables for backwards compatibility These are now wrappers around the centralized error system

View Source
var Context = ContextValue{
	"https://www.w3.org/ns/activitystreams",
	map[string]any{
		"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
		"sensitive":                 "as:sensitive",
		"Hashtag":                   "as:Hashtag",
		"quoteUrl":                  "as:quoteUrl",
		"toot":                      "http://joinmastodon.org/ns#",
		"Emoji":                     "toot:Emoji",
		"featured":                  "toot:featured",
		"discoverable":              "toot:discoverable",
		"schema":                    "http://schema.org#",
		"PropertyValue":             "schema:PropertyValue",
		"value":                     "schema:value",
		"agentManifest":             "https://lesser.social/ns/agentManifest",
	},
}

Context represents the richer JSON-LD context for ActivityStreams with Mastodon extensions.

View Source
var DefaultContext = ContextValue{"https://www.w3.org/ns/activitystreams"}

DefaultContext represents the bare ActivityStreams context.

Functions

func SanitizeHTML

func SanitizeHTML(content string) string

SanitizeHTML removes potentially dangerous HTML from content Uses bluemonday for robust XSS prevention

func SanitizeHTMLRelaxed

func SanitizeHTMLRelaxed(content string) string

SanitizeHTMLRelaxed applies a more relaxed sanitization policy Only use this for content from trusted sources

func ValidateActivity

func ValidateActivity(activity *Activity) error

ValidateActivity validates an Activity object

func ValidateActor

func ValidateActor(actor *Actor) error

ValidateActor validates an Actor object

func ValidateActorID

func ValidateActorID(actorID string) error

ValidateActorID validates an ActivityPub actor ID URL

func ValidateAddressing

func ValidateAddressing(addresses []string, fieldName string) error

ValidateAddressing validates addressing fields (to, cc, etc.)

func ValidateDomain

func ValidateDomain(domain string) error

ValidateDomain validates a domain name format

func ValidateNote

func ValidateNote(note *Note) error

ValidateNote validates a Note object

func ValidateURL

func ValidateURL(urlStr string, fieldName string) error

ValidateURL validates that a string is a valid URL

func ValidateUsername

func ValidateUsername(username string) error

ValidateUsername validates an ActivityPub username format

func ValidateWebfinger

func ValidateWebfinger(resource string) error

ValidateWebfinger validates a WebFinger resource identifier

Types

type Accept

type Accept struct {
	Activity
}

Accept represents an accept activity

type Activity

type Activity struct {
	BaseObject
	Actor      string `json:"actor"`
	Object     any    `json:"object"`
	Target     string `json:"target,omitempty"`     // For Add/Remove activities
	Origin     string `json:"origin,omitempty"`     // For Move activities
	Instrument string `json:"instrument,omitempty"` // For various activities
}

Activity represents an ActivityPub activity

func NewActivity

func NewActivity(activityType, id, actorID string, object any) *Activity

NewActivity creates a new activity

func ParseActivity

func ParseActivity(data []byte) (*Activity, error)

ParseActivity parses a JSON byte array into an Activity struct

type Actor

type Actor struct {
	BaseObject
	PreferredUsername string     `json:"preferredUsername"`
	Name              string     `json:"name,omitempty"`
	Summary           string     `json:"summary,omitempty"`
	URL               string     `json:"url,omitempty"`
	Icon              *Image     `json:"icon,omitempty"`
	Image             *Image     `json:"image,omitempty"`
	Inbox             string     `json:"inbox"`
	Outbox            string     `json:"outbox"`
	Following         string     `json:"following,omitempty"`
	Followers         string     `json:"followers,omitempty"`
	Liked             string     `json:"liked,omitempty"`
	PublicKey         *PublicKey `json:"publicKey,omitempty"`
	Endpoints         *Endpoints `json:"endpoints,omitempty"`

	// Account migration support
	AlsoKnownAs []string `json:"alsoKnownAs,omitempty"`
	MovedTo     string   `json:"movedTo,omitempty"`

	// Mastodon-specific fields
	ManuallyApprovesFollowers bool         `json:"manuallyApprovesFollowers,omitempty"`
	Discoverable              bool         `json:"discoverable,omitempty"`
	Featured                  string       `json:"featured,omitempty"`
	LastStatusAt              *time.Time   `json:"lastStatusAt,omitempty"`
	CreatedAt                 *time.Time   `json:"createdAt,omitempty"`
	Attachment                []Attachment `json:"attachment,omitempty"`

	// Lesser extension: optional agent metadata for Service actors.
	AgentManifest *AgentManifest `json:"agentManifest,omitempty"`
}

Actor represents an ActivityPub actor (Person, Service, etc.)

func NewActor

func NewActor(actorType, id, username string) *Actor

NewActor creates a new actor with required fields

func ParseActor

func ParseActor(data []byte) (*Actor, error)

ParseActor parses a JSON byte array into an Actor struct

func (*Actor) MarshalJSON

func (a *Actor) MarshalJSON() ([]byte, error)

MarshalJSON provides custom JSON marshaling to handle the context

type Add

type Add struct {
	Activity
	Target string `json:"target"` // The collection to add to
}

Add represents an add activity for adding items to collections

type AddressingValidator

type AddressingValidator struct{}

AddressingValidator provides validation for ActivityPub addressing fields

func NewAddressingValidator

func NewAddressingValidator() *AddressingValidator

NewAddressingValidator creates a new addressing validator

func (*AddressingValidator) DetermineDeliveryRecipients

func (v *AddressingValidator) DetermineDeliveryRecipients(activity *Activity) *DeliveryTargets

DetermineDeliveryRecipients determines who should receive the activity with shared inbox optimization

func (*AddressingValidator) GetDeliveryRecipientsForPrivacy

func (v *AddressingValidator) GetDeliveryRecipientsForPrivacy(activity *Activity, requestingActor string) []string

GetDeliveryRecipientsForPrivacy returns recipients based on privacy level

func (*AddressingValidator) GetVisibilityLevel

func (v *AddressingValidator) GetVisibilityLevel(activity *Activity) string

GetVisibilityLevel determines the privacy level of an activity

func (*AddressingValidator) IsDirectMessage

func (v *AddressingValidator) IsDirectMessage(activity *Activity) bool

IsDirectMessage checks if an activity represents a direct message

func (*AddressingValidator) IsFollowersOnlyMessage

func (v *AddressingValidator) IsFollowersOnlyMessage(activity *Activity) bool

IsFollowersOnlyMessage checks if the activity is addressed only to followers

func (*AddressingValidator) IsPrivateMessage

func (v *AddressingValidator) IsPrivateMessage(activity *Activity) bool

IsPrivateMessage checks if an activity is private/followers-only

func (*AddressingValidator) IsPublicMessage

func (v *AddressingValidator) IsPublicMessage(activity *Activity) bool

IsPublicMessage checks if an activity is public

func (*AddressingValidator) IsUnlistedMessage

func (v *AddressingValidator) IsUnlistedMessage(activity *Activity) bool

IsUnlistedMessage checks if an activity is unlisted

func (*AddressingValidator) SanitizeForDelivery

func (v *AddressingValidator) SanitizeForDelivery(activity *Activity, excludeBTo bool) *Activity

SanitizeForDelivery removes BCC recipients from activity before delivery

func (*AddressingValidator) ValidateAddressing

func (v *AddressingValidator) ValidateAddressing(activity *Activity) error

ValidateAddressing validates the addressing fields of an activity

func (*AddressingValidator) ValidatePrivacyCompliance

func (v *AddressingValidator) ValidatePrivacyCompliance(activity *Activity) error

ValidatePrivacyCompliance ensures activity respects privacy settings

type AgentCapabilities

type AgentCapabilities struct {
	CanPost   bool `json:"canPost"`
	CanReply  bool `json:"canReply"`
	CanBoost  bool `json:"canBoost"`
	CanFollow bool `json:"canFollow"`
	CanDM     bool `json:"canDM"`

	RestrictedDomains []string `json:"restrictedDomains,omitempty"`
	MaxPostsPerHour   int      `json:"maxPostsPerHour,omitempty"`
	RequiresApproval  bool     `json:"requiresApproval,omitempty"`
}

AgentCapabilities describe what an agent claims to be able to do. These are informational and should not be treated as authorization.

type AgentManifest

type AgentManifest struct {
	Type         string             `json:"type"`
	Version      string             `json:"version,omitempty"`
	Capabilities *AgentCapabilities `json:"capabilities,omitempty"`
	Purpose      string             `json:"purpose,omitempty"`
	OperatedBy   string             `json:"operatedBy,omitempty"`
	Source       string             `json:"source,omitempty"`
}

AgentManifest is a Lesser-specific ActivityPub extension describing an LLM agent account.

It is intentionally optional and designed to degrade gracefully on remote servers that ignore unknown fields.

type AgentPostAttribution

type AgentPostAttribution struct {
	TriggerType    string `json:"trigger_type,omitempty"`
	TriggerDetails string `json:"trigger_details,omitempty"`

	MemoryCitations []string `json:"memory_citations,omitempty"`

	DelegatedBy string   `json:"delegated_by,omitempty"`
	Scopes      []string `json:"scopes,omitempty"`

	Constraints  []string `json:"constraints,omitempty"`
	ModelVersion string   `json:"model_version,omitempty"`
}

AgentPostAttribution captures transparency metadata for an agent-authored post.

This is a Lesser extension. It is stored on the underlying Note and exposed via the REST API as `agent_attribution`.

type Announce

type Announce struct {
	Activity
}

Announce represents an announce (boost/share) activity

type Article

type Article struct {
	Note
	Name string `json:"name"` // Title
}

Article represents long-form content

type Attachment

type Attachment struct {
	Type      string `json:"type"`
	MediaType string `json:"mediaType"`
	URL       string `json:"url"`
	Name      string `json:"name,omitempty"`
	Value     string `json:"value,omitempty"` // For profile metadata
	Width     int    `json:"width,omitempty"`
	Height    int    `json:"height,omitempty"`
}

Attachment represents media attachments

type BaseObject

type BaseObject struct {
	Context   ContextValue `json:"@context,omitempty"`
	ID        string       `json:"id"`
	Type      string       `json:"type"`
	Published *time.Time   `json:"published,omitempty"`
	Updated   *time.Time   `json:"updated,omitempty"`
	To        []string     `json:"to,omitempty"`
	CC        []string     `json:"cc,omitempty"`
	BTo       []string     `json:"bto,omitempty"`
	BCC       []string     `json:"bcc,omitempty"`
	InReplyTo string       `json:"inReplyTo,omitempty"`
	Summary   string       `json:"summary,omitempty"`
	Sensitive bool         `json:"sensitive,omitempty"`
}

BaseObject represents the base ActivityStreams object

type Collection

type Collection struct {
	BaseObject
	TotalItems   int    `json:"totalItems"`
	Current      string `json:"current,omitempty"`
	First        string `json:"first,omitempty"`
	Last         string `json:"last,omitempty"`
	Items        any    `json:"items,omitempty"`
	OrderedItems any    `json:"orderedItems,omitempty"`
}

Collection represents an ActivityStreams collection

type CollectionPage

type CollectionPage struct {
	Collection
	PartOf string `json:"partOf"`
	Next   string `json:"next,omitempty"`
	Prev   string `json:"prev,omitempty"`
}

CollectionPage represents a page of a collection

type ContextValue

type ContextValue []any

ContextValue wraps the ActivityPub @context field to support both legacy string and modern array representations.

func (ContextValue) Clone

func (c ContextValue) Clone() ContextValue

Clone returns a shallow copy of the context slice so callers can safely append without mutating the shared base context.

func (ContextValue) MarshalJSON

func (c ContextValue) MarshalJSON() ([]byte, error)

MarshalJSON persists the context as a string when a single string value is present, otherwise it writes an array to match ActivityPub expectations.

func (*ContextValue) UnmarshalJSON

func (c *ContextValue) UnmarshalJSON(data []byte) error

UnmarshalJSON accepts either a single string or an array of values.

func (ContextValue) With

func (c ContextValue) With(values ...any) ContextValue

With returns a new context that includes the receiver's entries plus the provided ones.

type Create

type Create struct {
	Activity
}

Create represents a create activity

type Delete

type Delete struct {
	Activity
}

Delete represents a delete activity

type DeliveryTargets

type DeliveryTargets struct {
	DirectRecipients map[string]bool     // Individual actor inboxes
	SharedInboxes    map[string]bool     // Shared inboxes (domain-level)
	DomainGroups     map[string][]string // Recipients grouped by domain for shared inbox optimization
}

DeliveryTargets represents the recipients for federation delivery

func (*DeliveryTargets) GetAllRecipients

func (dt *DeliveryTargets) GetAllRecipients() []string

GetAllRecipients returns all unique recipients

type Endpoints

type Endpoints struct {
	SharedInbox string `json:"sharedInbox,omitempty"`
}

Endpoints represents an actor's endpoints

type Flag

type Flag struct {
	Activity
}

Flag represents a flag activity for content moderation

type Follow

type Follow struct {
	Activity
}

Follow represents a follow activity

type Image

type Image struct {
	BaseObject
	URL       string `json:"url"`
	MediaType string `json:"mediaType,omitempty"`
	Width     int    `json:"width,omitempty"`
	Height    int    `json:"height,omitempty"`
}

Image represents an image object

type Like

type Like struct {
	Activity
}

Like represents a like activity

type Move

type Move struct {
	Activity
	Target string `json:"target"` // The new account location
}

Move represents a move activity for account migration

type Note

type Note struct {
	BaseObject
	Content            string        `json:"content"`
	AttributedTo       string        `json:"attributedTo"`
	Attachment         []Attachment  `json:"attachment,omitempty"`
	Tag                []Tag         `json:"tag,omitempty"`
	ConversationID     string        `json:"conversationId,omitempty"` // For tracking conversation threads
	Visibility         string        `json:"_:visibility,omitempty"`   // Lesser extension for preserving visibility
	QuoteURL           string        `json:"quoteUrl,omitempty"`
	Quoteable          bool          `json:"_:quoteable,omitempty"`
	QuoteNotifications bool          `json:"_:quoteNotifications,omitempty"`
	QuoteContext       *QuoteContext `json:"_:quoteContext,omitempty"`

	// Lesser extension: per-status attribution for agent-authored content.
	AgentAttribution *AgentPostAttribution `json:"_:agentAttribution,omitempty"`
}

Note represents a basic text post

func ParseNote

func ParseNote(data []byte) (*Note, error)

ParseNote parses a JSON byte array into a Note struct

type OrderedCollection

type OrderedCollection struct {
	Collection
}

OrderedCollection represents an ordered ActivityStreams collection

type OrderedCollectionPage

type OrderedCollectionPage struct {
	CollectionPage
}

OrderedCollectionPage represents a page of an ordered collection

type PublicKey

type PublicKey struct {
	ID           string `json:"id"`
	Owner        string `json:"owner"`
	PublicKeyPem string `json:"publicKeyPem"`
}

PublicKey represents an actor's public key for HTTP signatures

type QuoteContext

type QuoteContext struct {
	OriginalNoteID         string `json:"originalNoteId,omitempty"`
	OriginalAuthor         string `json:"originalAuthor"`
	OriginalAuthorUsername string `json:"originalAuthorUsername,omitempty"`
	QuoteCount             int    `json:"quoteCount"`
	AllowWithdrawal        bool   `json:"allowWithdrawal"`
	QuoteAllowed           bool   `json:"quoteAllowed"`
	Withdrawn              bool   `json:"withdrawn"`
	OriginalStatus         any    `json:"-"`
}

QuoteContext provides metadata about a quoted note

type QuoteNote

type QuoteNote struct {
	Note
}

QuoteNote is retained for backwards compatibility; it now simply aliases Note.

type Reject

type Reject struct {
	Activity
}

Reject represents a reject activity

type Remove

type Remove struct {
	Activity
	Target string `json:"target"` // The collection to remove from
}

Remove represents a remove activity for removing items from collections

type Tag

type Tag struct {
	Type string `json:"type"`
	Href string `json:"href,omitempty"`
	Name string `json:"name"`
}

Tag represents hashtags or mentions

type Tombstone

type Tombstone struct {
	BaseObject
	FormerType string `json:"formerType,omitempty"` // The original object type
	Deleted    string `json:"deleted,omitempty"`    // ISO 8601 timestamp of deletion
}

Tombstone represents a deleted object placeholder per ActivityPub spec

type Undo

type Undo struct {
	Activity
}

Undo represents an undo activity

type Update

type Update struct {
	Activity
}

Update represents an update activity

type WebFingerLink struct {
	Rel      string `json:"rel"`
	Type     string `json:"type,omitempty"`
	Href     string `json:"href,omitempty"`
	Template string `json:"template,omitempty"`
}

WebFingerLink represents a link in a WebFinger response

type WebFingerResource

type WebFingerResource struct {
	Subject string          `json:"subject"`
	Aliases []string        `json:"aliases,omitempty"`
	Links   []WebFingerLink `json:"links"`
}

WebFingerResource represents a WebFinger response

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL