Something went wrong. Try again.
ActivityPub library in OCaml
Something went wrong. Try again.
OCaml 97%
2%
Dune <1%
Shell <1%
Makefile <1%
Standard ML <1%
README.md
ActivityPub OCaml #
A complete ActivityPub implementation in OCaml with effects-based I/O abstraction.
Overview #
This project provides:
- JSON-LD library (
jsonld) - Full JSON-LD 1.1 processing (expansion, compaction, context handling) - Activity Streams (
activity_streams) - Core Activity Streams 2.0 types - Activity Vocabulary (
activity_vocab) - Complete Activity Vocabulary types (Object, Actor, Activity, Collection) - ActivityPub (
activitypub) - ActivityPub protocol implementation with effects - ActivityPub EIO (
activitypub_eio) - Effect handlers using Eio for async I/O - HTTP Signatures (
http_signatures) - HTTP Signatures for request authentication
Building #
# Build everything
make build
# or
dune build
# Run unit tests
make test
# or
dune runtest
Components #
Test Server (ap-server) #
A minimal ActivityPub server for testing and conformance validation:
# Start the server (default port 9002)
make server
# Custom port
AP_PORT=3000 make server
# or
AP_PORT=3000 dune exec ap-server
Endpoints:
GET /actors/:name- Actor profile (Person)GET /actors/:name/inbox- Actor inbox (OrderedCollection)GET /actors/:name/outbox- Actor outbox (OrderedCollection)POST /actors/:name/outbox- Post activity/object to outboxGET /actors/:name/followers- Followers collectionGET /actors/:name/following- Following collectionGET /actors/:name/liked- Liked collectionGET /actors/:name/objects/:id- Get object by IDGET /actors/:name/outbox/activities/:id- Get activity by IDGET /.well-known/webfinger?resource=acct:name@host- WebFinger discovery
CLI Client (ap-cli) #
Command-line client for interacting with ActivityPub servers:
# Fetch an actor profile
dune exec ap-cli -- fetch https://mastodon.social/@user
# Get an actor's posts
dune exec ap-cli -- posts https://mastodon.social/@user
# Fetch any ActivityPub resource
dune exec ap-cli -- get https://example.com/activity/123
# WebFinger lookup
dune exec ap-cli -- webfinger user@mastodon.social
# Create a Note (posts to outbox)
dune exec ap-cli -- post https://example.com/actors/me "Hello world!"
# Follow an actor
dune exec ap-cli -- follow https://example.com/actors/me https://other.com/actors/them
# Like an object
dune exec ap-cli -- like https://example.com/actors/me https://other.com/posts/123
Conformance Testing #
This implementation passes the activitypub-testing conformance suite.
Running Conformance Tests #
# Install test dependencies (first time only)
npm install
# Run conformance tests (starts server automatically)
make conformance
# With custom port or actor
make conformance PORT=9003 ACTOR=alice
Manual Testing #
# Terminal 1: Start the server
AP_PORT=9002 dune exec ap-server
# Terminal 2: Run conformance tests
npx activitypub-testing test actor http://127.0.0.1:9002/actors/test
Conformance Results #
The test server passes core ActivityPub requirements:
- Actor endpoints with proper JSON-LD context
- Inbox/Outbox as OrderedCollections
- Collection pagination support
- Outbox POST with Create wrapping
- Activity/Object ID assignment
- Update activity handling
Library Usage #
Using the ActivityPub Library #
open Activitypub
open Activity_vocabulary
(* Define effect handlers *)
let run_with_handlers f =
Activitypub_eio.run f
(* Fetch an actor *)
let fetch_actor url =
run_with_handlers (fun () ->
match Discovery.finger url with
| Ok actor -> Printf.printf "Found: %s\n" (Actor.name actor)
| Error e -> Printf.printf "Error: %s\n" e
)
(* Create an activity *)
let create_note actor_url content =
let note = Object.note ~content () in
let create = Activity.create ~actor:actor_url ~obj:note () in
run_with_handlers (fun () ->
Outbox.post actor_url create
)
Effects System #
The library uses OCaml 5 effects for I/O abstraction:
(* Effect signatures in Activitypub.Effects *)
type _ Effect.t +=
| Http_get : string -> (string, string) result Effect.t
| Http_post : string * string * string -> (string, string) result Effect.t
| Store_get : string -> Jsont.json option Effect.t
| Store_put : string * Jsont.json -> unit Effect.t
| Time_now : float Effect.t
| Log : string -> unit Effect.t
(* ... *)
(* Provide your own handlers *)
let my_handler : type a. a Effect.t -> a = function
| Http_get url -> (* your HTTP implementation *)
| Http_post (url, content_type, body) -> (* ... *)
| _ -> Effect.perform (* delegate to default *)
Project Structure #
lib/
jsonld/ # JSON-LD 1.1 processing
activity_streams/ # Activity Streams 2.0 core
activity_vocab/ # Activity Vocabulary types
activitypub/ # ActivityPub protocol
activitypub_eio/ # Eio effect handlers
http_signatures/ # HTTP Signatures
bin/
ap_server/ # Test server
ap_cli/ # CLI client
test/
jsonld/ # JSON-LD tests
activity_vocab/ # Vocabulary tests
activitypub/ # Protocol tests
Development #
# Build
make build
# Test
make test
# Format code
make fmt
# Check formatting
make fmt-check
# Clean
make clean
Dependencies #
OCaml libraries (via opam):
eio,eio_main- Effects-based async I/Ocohttp-eio- HTTP client/serverdream- Web framework (test server)jsont- JSON encoding/decodinguri- URI handlingalcotest- Testing framework
License #
See LICENSE file.