Skip to main content

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.py to pyproject.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
  • Async-by-default API with sync wrappers for Flask/Django compatibility

Modernization Features

Python 3.10+ Features

  • Structural Pattern Matching (match/case statements)
  • Modern Union Types (X | Y syntax instead of Union[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% code coverage
  • Modern code formatting with Black
  • Strict linting with Ruff
  • Type checking with MyPy

Testing

  • ActivityPub protocol compliance testing (core + extended activities)
  • Integration tests with mock servers
  • Property-based testing for robustness
  • Security-focused test suite (~89% coverage)

Implemented ActivityPub Features

Core Activities [x]

Create, Update, Delete, Follow, Accept, Reject, Add, Remove, Like, Block, Undo, Announce

Extended Activities [x]

Flag, Move, Join, Leave, View, Listen, Read, Write, Travel, Arrive

Actor Properties [x]

inbox, outbox, following, followers, preferredUsername, endpoints (sharedInbox)

Collections [x]

Collection, OrderedCollection, CollectionPage, OrderedCollectionPage

Security [x]

HTTP Signatures (generation/verification), Linked Data Signatures

Plugin Interface [x]

active_boxes.plugin.ActivityPubPlugin - Protocol defining app responsibilities

Missing (Under Development)

  • Per-object Likes/Shares collections
  • Backward pagination in collections
  • Featured collection support

Quick Start

This is an async-first library - the primary API uses async/await. Sync wrappers (e.g., fetch_iri_sync()) are available for Flask/Django compatibility.

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

Async (Recommended for FastAPI, aiohttp, etc.):

# 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 = await fetch_actor(create.get_actor().id)
    await plugin.deliver_activity(create.to_dict(), inbox, actor)

Sync (For Flask, Django sync views):

# 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 (sync wrapper)
recipients = create.recipients()
for inbox in recipients:
    actor = fetch_actor_sync(create.get_actor_sync().id)
    plugin.deliver_activity(create.to_dict(), inbox, actor)

4. Receive Activities

Async (FastAPI, aiohttp):

# 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)

Sync (Flask, Django sync views):

# In your Flask route
@app.post("/inbox")
def inbox():
    activity = request.get_json()
    plugin.receive_activity_sync(activity, source_inbox=request.url)
    return "", 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 (async)
items = await backend.parse_collection(url="https://example.com/user/bob/outbox")

# Or use sync wrapper
items = backend.parse_collection_sync(url="https://example.com/user/bob/outbox")

API Naming Convention

The library uses an async-first naming convention:

Operation Async (Primary) Sync Wrapper
Fetch IRI fetch_iri() fetch_iri_sync()
Fetch JSON fetch_json() fetch_json_sync()
Get Actor get_actor() get_actor_sync()
Get Object get_object() get_object_sync()
WebFinger webfinger() webfinger_sync()
Verify Signature verify_request() verify_request_sync()
Parse Collection parse_collection() parse_collection_sync()

Guideline: Use async methods by default. Use _sync() variants only when integrating with sync frameworks like Flask or Django sync views.

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:

Original Project

For information about the original project, please refer to ORIGINAL-README.md.

Metadata

Release files for active-boxes 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for active-boxes 0.2.0
File Size Uploaded
active_boxes-0.2.0.tar.gz 42.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for active-boxes 0.2.0
File Interpreter ABI Platform
active_boxes-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 89.0 kB

Release files / active_boxes-0.2.0.tar.gz

Download URL active_boxes-0.2.0.tar.gz
Size 42.2 kB
Tags Source
SHA-256 checksum
How to use checksums
5499690a1f0d5f9ef69ca6c853e5919c96aaa531fb48bca20c282675549a5a92
BLAKE2b-256 checksum
How to use checksums
da7ae69fb5fc995a12458f9fd592420e4164cd9b387d90854edd4fa4b0679c98
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.4.1 CPython/3.14.7 Darwin/25.6.0

Release files / active_boxes-0.2.0-py3-none-any.whl

Download URL active_boxes-0.2.0-py3-none-any.whl
Size 46.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1b55fe9026d1ace5c56742c053fe24f1f027db601826cc27044b78cfb4e3d446
BLAKE2b-256 checksum
How to use checksums
10b96b8a4572365145e6e7bbfe5eddf07c70d56fe479d0c8d51c8874ef1a678e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.4.1 CPython/3.14.7 Darwin/25.6.0

Release history Release notifications | RSS feed

0.2.6

2 release files

0.2.5

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page