Tiny ActivityPub framework written in Python, both database and server agnostic.
Project description
Active Boxes (Modernized Little Boxes)
This project is a fork of Little Boxes that has been modernized and relicensed from ISC to MIT.
⚠️ Modernization Complete, ActivityPub Compliance In Progress ⚠️
This project has been successfully modernized and updated to current Python packaging standards and Python 3.10+ features. Core ActivityPub functionality is implemented, with federation delivery features under development.
The original README can be found in ORIGINAL-README.md.
Current Status
- Migrated from
setup.pytopyproject.toml - Moved development dependencies to
pyproject.toml - Switched to Poetry for dependency management and building
- Updated to require Python 3.10+
- Created comprehensive modernization plans
- Modernized codebase to leverage Python 3.10+ features
- Created comprehensive test suite
- [~] ActivityPub protocol compliance - Core 11 activities ✅, Extended activities ⚠️
- Updated documentation and examples
- Prepared for stable release
Modernization Features
Python 3.10+ Features
- Structural Pattern Matching (match/case statements)
- Modern Union Types (
X | Ysyntax instead ofUnion[X, Y]) - Parenthesized context managers
- Improved type hinting throughout the codebase
- Walrus operator usage where appropriate
- Modern string formatting with f-strings
Code Quality
- 100% type hinting coverage
- Comprehensive test suite with ~89% coverage
- Modern code formatting with Black
- Strict linting with Ruff
- Type checking with MyPy
Testing
- ActivityPub protocol compliance testing (core activities)
- Integration tests with mock servers
- Property-based testing for robustness
- Security-focused test suite (~89% coverage)
Implemented ActivityPub Features
Core Activities ✅
Create, Update, Delete, Follow, Accept, Reject, Add, Remove, Like, Block, Undo, Announce
Actor Properties ✅
inbox, outbox, following, followers, preferredUsername, endpoints (sharedInbox)
Collections ✅
Collection, OrderedCollection, CollectionPage, OrderedCollectionPage
Security ✅
HTTP Signatures (generation/verification), Linked Data Signatures
Plugin Interface ✅
active_boxes.plugin.ActivityPubPlugin - Protocol defining app responsibilities
Missing (Under Development)
- Extended activities: Flag, Move, Join, Leave, View, Listen, Read, Write, Travel, Arrive
- Per-object Likes/Shares collections
- Backward pagination in collections
Quick Start
This is an async library - your plugin should use asyncio or any async framework (FastAPI, aiohttp, etc.).
1. Implement the Plugin Protocol
from active_boxes import activitypub as ap
from active_boxes.plugin import ActivityPubPlugin
class MyAppPlugin(ActivityPubPlugin):
BASE_URL = "https://myapp.example"
# Required: URL generation
def base_url(self) -> str:
return self.BASE_URL
def activity_url(self, obj_id: str) -> str:
return f"{self.BASE_URL}/activity/{obj_id}"
def note_url(self, obj_id: str) -> str:
return f"{self.BASE_URL}/note/{obj_id}"
# Required: Deliver activities to remote inboxes
async def deliver_activity(
self,
activity: dict,
inbox: str,
actor: dict,
) -> bool:
signed = self.sign_request(activity, actor)
async with httpx.AsyncClient() as client:
resp = await client.post(inbox, json=signed)
return resp.status_code in (200, 201, 202)
# Required: Process incoming activities
async def receive_activity(
self,
activity: dict,
source_inbox: str | None = None,
) -> bool:
if self.is_duplicate(activity["id"]):
return False # Skip duplicate
await self.store_activity(activity, source_inbox)
await self.process_activity(activity)
return True
# Required: Deduplication
def is_duplicate(self, activity_id: str) -> bool:
return self.redis.exists(f"activity:{activity_id}")
# Optional: Add extra recipients for all activities
def extra_inboxes(self) -> list[str]:
return [] # Or add a shared inbox
def sign_request(self, activity: dict, actor: dict) -> dict:
# Your HTTP signature logic here
...
2. Initialize the Backend
from active_boxes import activitypub as ap
plugin = MyAppPlugin()
ap.use_backend(plugin)
3. Create and Send Activities
# Create a note
note = ap.Note(
content="Hello, federation!",
attributedTo="https://myapp.example/user/alice",
to=[ap.AS_PUBLIC],
)
# Create the activity wrapping the note
create = note.build_create()
create.set_id("https://myapp.example/activity/abc123", "abc123")
# Get recipients and deliver
recipients = create.recipients() # Computed by library
for inbox in recipients:
actor = fetch_actor(create.get_actor().id)
await plugin.deliver_activity(create.to_dict(), inbox, actor)
4. Receive Activities
# In your inbox endpoint handler
async def inbox_handler(request):
activity = await request.json()
await plugin.receive_activity(activity, source_inbox=str(request.url))
return web.Response(status=202)
5. Working with Actors
# Create a person actor
person = ap.Person(
id="https://myapp.example/user/alice",
inbox="https://myapp.example/user/alice/inbox",
outbox="https://myapp.example/user/alice/outbox",
followers="https://myapp.example/user/alice/followers",
preferredUsername="alice",
publicKey={
"id": "https://myapp.example/user/alice#main-key",
"owner": "https://myapp.example/user/alice",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----...",
},
)
6. Collection Pagination
# Build a paginated outbox
outbox = ap.OrderedCollection(
id="https://myapp.example/user/alice/outbox",
totalItems=42,
first="https://myapp.example/user/alice/outbox?page=1",
)
# Library handles parsing remote collections
items = backend.parse_collection(url="https://example.com/user/bob/outbox")
Plugin Responsibilities
| What Library Does | What Your App Does |
|---|---|
| Activity/Object serialization | HTTP client setup (httpx, aiohttp, etc.) |
| Computing recipients | Signing outgoing requests (HTTP Signatures) |
| HTTP Signature generation | Delivering to remote inboxes |
| HTTP Signature verification | Receiving from remote inboxes |
| Collection pagination | Storing activities persistently |
| Activity vocabulary (Create, Follow, etc.) | Deduplication |
| WebFinger support | Retry/backoff logic |
Modernization Plans
Detailed planning documents have been created to guide the modernization effort:
- MODERNIZE_PLAN.md - Overall modernization strategy
- PYTHON_310_MODERNIZATION.md - Python 3.10+ feature implementation
- TEST_SUITE_IMPROVEMENTS.md - Test suite enhancement plans
- ACTIVITYPUB_COMPLIANCE.md - ActivityPub protocol compliance requirements
- IMPLEMENTATION_PLAN.md - Detailed 8-week implementation timeline
Original Project
For information about the original project, please refer to ORIGINAL-README.md.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file active_boxes-0.1.0.tar.gz.
File metadata
- Download URL: active_boxes-0.1.0.tar.gz
- Upload date:
- Size: 23.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.2 CPython/3.14.3 Linux/6.19.8-200.fc43.x86_64
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2e1da27983b223a8f547672067a1be56ee4ee482165c7117ee4c0607d20fe47c
|
|
| MD5 |
33b2ce61019ea31aa1d69303ccdc6cec
|
|
| BLAKE2b-256 |
26379562d141dabe6423731f8807b2edecf8e1439e199de8c9a94029715e5cb3
|
File details
Details for the file active_boxes-0.1.0-py3-none-any.whl.
File metadata
- Download URL: active_boxes-0.1.0-py3-none-any.whl
- Upload date:
- Size: 26.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.2 CPython/3.14.3 Linux/6.19.8-200.fc43.x86_64
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
66f4614090ab2d0328cb3e6fe98315084fa28366bbc16e065bfe346705a23174
|
|
| MD5 |
a448c1864b2607738e1be6ba45bf3715
|
|
| BLAKE2b-256 |
bb5da4f3805eba8b022527993ffcfc0a3f8be9fc3880514073b22ac627125649
|