active-boxes 0.1.0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {active_boxes-0.1.0 → active_boxes-0.2.0}/PKG-INFO +76 -15
- {active_boxes-0.1.0 → active_boxes-0.2.0}/README.md +74 -14
- {active_boxes-0.1.0 → active_boxes-0.2.0}/active_boxes/activitypub.py +636 -101
- active_boxes-0.2.0/active_boxes/backend.py +252 -0
- active_boxes-0.2.0/active_boxes/collection.py +396 -0
- {active_boxes-0.1.0 → active_boxes-0.2.0}/active_boxes/content_helper.py +13 -14
- active_boxes-0.2.0/active_boxes/data_integrity.py +214 -0
- {active_boxes-0.1.0 → active_boxes-0.2.0}/active_boxes/errors.py +2 -3
- active_boxes-0.2.0/active_boxes/http_client.py +549 -0
- active_boxes-0.2.0/active_boxes/httpsig.py +933 -0
- active_boxes-0.2.0/active_boxes/key.py +205 -0
- {active_boxes-0.1.0 → active_boxes-0.2.0}/active_boxes/linked_data_sig.py +51 -9
- {active_boxes-0.1.0 → active_boxes-0.2.0}/active_boxes/plugin.py +2 -2
- {active_boxes-0.1.0 → active_boxes-0.2.0}/active_boxes/urlutils.py +3 -9
- active_boxes-0.2.0/active_boxes/webfinger.py +172 -0
- {active_boxes-0.1.0 → active_boxes-0.2.0}/pyproject.toml +3 -2
- active_boxes-0.1.0/active_boxes/backend.py +0 -140
- active_boxes-0.1.0/active_boxes/collection.py +0 -70
- active_boxes-0.1.0/active_boxes/httpsig.py +0 -159
- active_boxes-0.1.0/active_boxes/key.py +0 -65
- active_boxes-0.1.0/active_boxes/webfinger.py +0 -91
- {active_boxes-0.1.0 → active_boxes-0.2.0}/LICENSE +0 -0
- {active_boxes-0.1.0 → active_boxes-0.2.0}/active_boxes/__init__.py +0 -0
- {active_boxes-0.1.0 → active_boxes-0.2.0}/active_boxes/__version__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: active-boxes
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Tiny ActivityPub framework written in Python, both database and server agnostic.
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
16
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
17
|
+
Requires-Dist: aiohttp (>=3.9.0)
|
|
17
18
|
Requires-Dist: bleach (>=6.0.0)
|
|
18
19
|
Requires-Dist: html2text (>=2020.1.16)
|
|
19
20
|
Requires-Dist: markdown (>=3.4.0)
|
|
@@ -29,7 +30,7 @@ Description-Content-Type: text/markdown
|
|
|
29
30
|
|
|
30
31
|
This project is a fork of [Little Boxes](https://github.com/tsileo/little-boxes) that has been modernized and relicensed from ISC to MIT.
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
**Modernization Complete, ActivityPub Compliance In Progress**
|
|
33
34
|
|
|
34
35
|
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.
|
|
35
36
|
|
|
@@ -44,9 +45,10 @@ The original README can be found in [ORIGINAL-README.md](ORIGINAL-README.md).
|
|
|
44
45
|
- [x] Created comprehensive modernization plans
|
|
45
46
|
- [x] Modernized codebase to leverage Python 3.10+ features
|
|
46
47
|
- [x] Created comprehensive test suite
|
|
47
|
-
- [
|
|
48
|
+
- [x] ActivityPub protocol compliance - Core 11 activities, Extended activities
|
|
48
49
|
- [x] Updated documentation and examples
|
|
49
50
|
- [x] Prepared for stable release
|
|
51
|
+
- [x] **Async-by-default API** with sync wrappers for Flask/Django compatibility
|
|
50
52
|
|
|
51
53
|
## Modernization Features
|
|
52
54
|
|
|
@@ -62,49 +64,53 @@ The original README can be found in [ORIGINAL-README.md](ORIGINAL-README.md).
|
|
|
62
64
|
### Code Quality
|
|
63
65
|
|
|
64
66
|
- 100% type hinting coverage
|
|
65
|
-
- Comprehensive test suite with ~89% coverage
|
|
67
|
+
- Comprehensive test suite with ~89% code coverage
|
|
66
68
|
- Modern code formatting with Black
|
|
67
69
|
- Strict linting with Ruff
|
|
68
70
|
- Type checking with MyPy
|
|
69
71
|
|
|
70
72
|
### Testing
|
|
71
73
|
|
|
72
|
-
- ActivityPub protocol compliance testing (core activities)
|
|
74
|
+
- ActivityPub protocol compliance testing (core + extended activities)
|
|
73
75
|
- Integration tests with mock servers
|
|
74
76
|
- Property-based testing for robustness
|
|
75
77
|
- Security-focused test suite (~89% coverage)
|
|
76
78
|
|
|
77
79
|
## Implemented ActivityPub Features
|
|
78
80
|
|
|
79
|
-
### Core Activities
|
|
81
|
+
### Core Activities [x]
|
|
80
82
|
|
|
81
83
|
Create, Update, Delete, Follow, Accept, Reject, Add, Remove, Like, Block, Undo, Announce
|
|
82
84
|
|
|
83
|
-
###
|
|
85
|
+
### Extended Activities [x]
|
|
86
|
+
|
|
87
|
+
Flag, Move, Join, Leave, View, Listen, Read, Write, Travel, Arrive
|
|
88
|
+
|
|
89
|
+
### Actor Properties [x]
|
|
84
90
|
|
|
85
91
|
inbox, outbox, following, followers, preferredUsername, endpoints (sharedInbox)
|
|
86
92
|
|
|
87
|
-
### Collections
|
|
93
|
+
### Collections [x]
|
|
88
94
|
|
|
89
95
|
Collection, OrderedCollection, CollectionPage, OrderedCollectionPage
|
|
90
96
|
|
|
91
|
-
### Security
|
|
97
|
+
### Security [x]
|
|
92
98
|
|
|
93
99
|
HTTP Signatures (generation/verification), Linked Data Signatures
|
|
94
100
|
|
|
95
|
-
### Plugin Interface
|
|
101
|
+
### Plugin Interface [x]
|
|
96
102
|
|
|
97
103
|
`active_boxes.plugin.ActivityPubPlugin` - Protocol defining app responsibilities
|
|
98
104
|
|
|
99
105
|
### Missing (Under Development)
|
|
100
106
|
|
|
101
|
-
- Extended activities: Flag, Move, Join, Leave, View, Listen, Read, Write, Travel, Arrive
|
|
102
107
|
- Per-object Likes/Shares collections
|
|
103
108
|
- Backward pagination in collections
|
|
109
|
+
- Featured collection support
|
|
104
110
|
|
|
105
111
|
## Quick Start
|
|
106
112
|
|
|
107
|
-
**This is an async library** -
|
|
113
|
+
**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.
|
|
108
114
|
|
|
109
115
|
### 1. Implement the Plugin Protocol
|
|
110
116
|
|
|
@@ -173,6 +179,8 @@ ap.use_backend(plugin)
|
|
|
173
179
|
|
|
174
180
|
### 3. Create and Send Activities
|
|
175
181
|
|
|
182
|
+
**Async (Recommended for FastAPI, aiohttp, etc.):**
|
|
183
|
+
|
|
176
184
|
```python
|
|
177
185
|
# Create a note
|
|
178
186
|
note = ap.Note(
|
|
@@ -188,12 +196,35 @@ create.set_id("https://myapp.example/activity/abc123", "abc123")
|
|
|
188
196
|
# Get recipients and deliver
|
|
189
197
|
recipients = create.recipients() # Computed by library
|
|
190
198
|
for inbox in recipients:
|
|
191
|
-
actor = fetch_actor(create.get_actor().id)
|
|
199
|
+
actor = await fetch_actor(create.get_actor().id)
|
|
192
200
|
await plugin.deliver_activity(create.to_dict(), inbox, actor)
|
|
193
201
|
```
|
|
194
202
|
|
|
203
|
+
**Sync (For Flask, Django sync views):**
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
# Create a note
|
|
207
|
+
note = ap.Note(
|
|
208
|
+
content="Hello, federation!",
|
|
209
|
+
attributedTo="https://myapp.example/user/alice",
|
|
210
|
+
to=[ap.AS_PUBLIC],
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# Create the activity wrapping the note
|
|
214
|
+
create = note.build_create()
|
|
215
|
+
create.set_id("https://myapp.example/activity/abc123", "abc123")
|
|
216
|
+
|
|
217
|
+
# Get recipients and deliver (sync wrapper)
|
|
218
|
+
recipients = create.recipients()
|
|
219
|
+
for inbox in recipients:
|
|
220
|
+
actor = fetch_actor_sync(create.get_actor_sync().id)
|
|
221
|
+
plugin.deliver_activity(create.to_dict(), inbox, actor)
|
|
222
|
+
```
|
|
223
|
+
|
|
195
224
|
### 4. Receive Activities
|
|
196
225
|
|
|
226
|
+
**Async (FastAPI, aiohttp):**
|
|
227
|
+
|
|
197
228
|
```python
|
|
198
229
|
# In your inbox endpoint handler
|
|
199
230
|
async def inbox_handler(request):
|
|
@@ -202,6 +233,17 @@ async def inbox_handler(request):
|
|
|
202
233
|
return web.Response(status=202)
|
|
203
234
|
```
|
|
204
235
|
|
|
236
|
+
**Sync (Flask, Django sync views):**
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
# In your Flask route
|
|
240
|
+
@app.post("/inbox")
|
|
241
|
+
def inbox():
|
|
242
|
+
activity = request.get_json()
|
|
243
|
+
plugin.receive_activity_sync(activity, source_inbox=request.url)
|
|
244
|
+
return "", 202
|
|
245
|
+
```
|
|
246
|
+
|
|
205
247
|
### 5. Working with Actors
|
|
206
248
|
|
|
207
249
|
```python
|
|
@@ -230,10 +272,29 @@ outbox = ap.OrderedCollection(
|
|
|
230
272
|
first="https://myapp.example/user/alice/outbox?page=1",
|
|
231
273
|
)
|
|
232
274
|
|
|
233
|
-
# Library handles parsing remote collections
|
|
234
|
-
items = backend.parse_collection(url="https://example.com/user/bob/outbox")
|
|
275
|
+
# Library handles parsing remote collections (async)
|
|
276
|
+
items = await backend.parse_collection(url="https://example.com/user/bob/outbox")
|
|
277
|
+
|
|
278
|
+
# Or use sync wrapper
|
|
279
|
+
items = backend.parse_collection_sync(url="https://example.com/user/bob/outbox")
|
|
235
280
|
```
|
|
236
281
|
|
|
282
|
+
## API Naming Convention
|
|
283
|
+
|
|
284
|
+
The library uses an **async-first** naming convention:
|
|
285
|
+
|
|
286
|
+
| Operation | Async (Primary) | Sync Wrapper |
|
|
287
|
+
|-----------|----------------|--------------|
|
|
288
|
+
| Fetch IRI | `fetch_iri()` | `fetch_iri_sync()` |
|
|
289
|
+
| Fetch JSON | `fetch_json()` | `fetch_json_sync()` |
|
|
290
|
+
| Get Actor | `get_actor()` | `get_actor_sync()` |
|
|
291
|
+
| Get Object | `get_object()` | `get_object_sync()` |
|
|
292
|
+
| WebFinger | `webfinger()` | `webfinger_sync()` |
|
|
293
|
+
| Verify Signature | `verify_request()` | `verify_request_sync()` |
|
|
294
|
+
| Parse Collection | `parse_collection()` | `parse_collection_sync()` |
|
|
295
|
+
|
|
296
|
+
**Guideline:** Use async methods by default. Use `_sync()` variants only when integrating with sync frameworks like Flask or Django sync views.
|
|
297
|
+
|
|
237
298
|
## Plugin Responsibilities
|
|
238
299
|
|
|
239
300
|
| What Library Does | What Your App Does |
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
This project is a fork of [Little Boxes](https://github.com/tsileo/little-boxes) that has been modernized and relicensed from ISC to MIT.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**Modernization Complete, ActivityPub Compliance In Progress**
|
|
6
6
|
|
|
7
7
|
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.
|
|
8
8
|
|
|
@@ -17,9 +17,10 @@ The original README can be found in [ORIGINAL-README.md](ORIGINAL-README.md).
|
|
|
17
17
|
- [x] Created comprehensive modernization plans
|
|
18
18
|
- [x] Modernized codebase to leverage Python 3.10+ features
|
|
19
19
|
- [x] Created comprehensive test suite
|
|
20
|
-
- [
|
|
20
|
+
- [x] ActivityPub protocol compliance - Core 11 activities, Extended activities
|
|
21
21
|
- [x] Updated documentation and examples
|
|
22
22
|
- [x] Prepared for stable release
|
|
23
|
+
- [x] **Async-by-default API** with sync wrappers for Flask/Django compatibility
|
|
23
24
|
|
|
24
25
|
## Modernization Features
|
|
25
26
|
|
|
@@ -35,49 +36,53 @@ The original README can be found in [ORIGINAL-README.md](ORIGINAL-README.md).
|
|
|
35
36
|
### Code Quality
|
|
36
37
|
|
|
37
38
|
- 100% type hinting coverage
|
|
38
|
-
- Comprehensive test suite with ~89% coverage
|
|
39
|
+
- Comprehensive test suite with ~89% code coverage
|
|
39
40
|
- Modern code formatting with Black
|
|
40
41
|
- Strict linting with Ruff
|
|
41
42
|
- Type checking with MyPy
|
|
42
43
|
|
|
43
44
|
### Testing
|
|
44
45
|
|
|
45
|
-
- ActivityPub protocol compliance testing (core activities)
|
|
46
|
+
- ActivityPub protocol compliance testing (core + extended activities)
|
|
46
47
|
- Integration tests with mock servers
|
|
47
48
|
- Property-based testing for robustness
|
|
48
49
|
- Security-focused test suite (~89% coverage)
|
|
49
50
|
|
|
50
51
|
## Implemented ActivityPub Features
|
|
51
52
|
|
|
52
|
-
### Core Activities
|
|
53
|
+
### Core Activities [x]
|
|
53
54
|
|
|
54
55
|
Create, Update, Delete, Follow, Accept, Reject, Add, Remove, Like, Block, Undo, Announce
|
|
55
56
|
|
|
56
|
-
###
|
|
57
|
+
### Extended Activities [x]
|
|
58
|
+
|
|
59
|
+
Flag, Move, Join, Leave, View, Listen, Read, Write, Travel, Arrive
|
|
60
|
+
|
|
61
|
+
### Actor Properties [x]
|
|
57
62
|
|
|
58
63
|
inbox, outbox, following, followers, preferredUsername, endpoints (sharedInbox)
|
|
59
64
|
|
|
60
|
-
### Collections
|
|
65
|
+
### Collections [x]
|
|
61
66
|
|
|
62
67
|
Collection, OrderedCollection, CollectionPage, OrderedCollectionPage
|
|
63
68
|
|
|
64
|
-
### Security
|
|
69
|
+
### Security [x]
|
|
65
70
|
|
|
66
71
|
HTTP Signatures (generation/verification), Linked Data Signatures
|
|
67
72
|
|
|
68
|
-
### Plugin Interface
|
|
73
|
+
### Plugin Interface [x]
|
|
69
74
|
|
|
70
75
|
`active_boxes.plugin.ActivityPubPlugin` - Protocol defining app responsibilities
|
|
71
76
|
|
|
72
77
|
### Missing (Under Development)
|
|
73
78
|
|
|
74
|
-
- Extended activities: Flag, Move, Join, Leave, View, Listen, Read, Write, Travel, Arrive
|
|
75
79
|
- Per-object Likes/Shares collections
|
|
76
80
|
- Backward pagination in collections
|
|
81
|
+
- Featured collection support
|
|
77
82
|
|
|
78
83
|
## Quick Start
|
|
79
84
|
|
|
80
|
-
**This is an async library** -
|
|
85
|
+
**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.
|
|
81
86
|
|
|
82
87
|
### 1. Implement the Plugin Protocol
|
|
83
88
|
|
|
@@ -146,6 +151,8 @@ ap.use_backend(plugin)
|
|
|
146
151
|
|
|
147
152
|
### 3. Create and Send Activities
|
|
148
153
|
|
|
154
|
+
**Async (Recommended for FastAPI, aiohttp, etc.):**
|
|
155
|
+
|
|
149
156
|
```python
|
|
150
157
|
# Create a note
|
|
151
158
|
note = ap.Note(
|
|
@@ -161,12 +168,35 @@ create.set_id("https://myapp.example/activity/abc123", "abc123")
|
|
|
161
168
|
# Get recipients and deliver
|
|
162
169
|
recipients = create.recipients() # Computed by library
|
|
163
170
|
for inbox in recipients:
|
|
164
|
-
actor = fetch_actor(create.get_actor().id)
|
|
171
|
+
actor = await fetch_actor(create.get_actor().id)
|
|
165
172
|
await plugin.deliver_activity(create.to_dict(), inbox, actor)
|
|
166
173
|
```
|
|
167
174
|
|
|
175
|
+
**Sync (For Flask, Django sync views):**
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
# Create a note
|
|
179
|
+
note = ap.Note(
|
|
180
|
+
content="Hello, federation!",
|
|
181
|
+
attributedTo="https://myapp.example/user/alice",
|
|
182
|
+
to=[ap.AS_PUBLIC],
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# Create the activity wrapping the note
|
|
186
|
+
create = note.build_create()
|
|
187
|
+
create.set_id("https://myapp.example/activity/abc123", "abc123")
|
|
188
|
+
|
|
189
|
+
# Get recipients and deliver (sync wrapper)
|
|
190
|
+
recipients = create.recipients()
|
|
191
|
+
for inbox in recipients:
|
|
192
|
+
actor = fetch_actor_sync(create.get_actor_sync().id)
|
|
193
|
+
plugin.deliver_activity(create.to_dict(), inbox, actor)
|
|
194
|
+
```
|
|
195
|
+
|
|
168
196
|
### 4. Receive Activities
|
|
169
197
|
|
|
198
|
+
**Async (FastAPI, aiohttp):**
|
|
199
|
+
|
|
170
200
|
```python
|
|
171
201
|
# In your inbox endpoint handler
|
|
172
202
|
async def inbox_handler(request):
|
|
@@ -175,6 +205,17 @@ async def inbox_handler(request):
|
|
|
175
205
|
return web.Response(status=202)
|
|
176
206
|
```
|
|
177
207
|
|
|
208
|
+
**Sync (Flask, Django sync views):**
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
# In your Flask route
|
|
212
|
+
@app.post("/inbox")
|
|
213
|
+
def inbox():
|
|
214
|
+
activity = request.get_json()
|
|
215
|
+
plugin.receive_activity_sync(activity, source_inbox=request.url)
|
|
216
|
+
return "", 202
|
|
217
|
+
```
|
|
218
|
+
|
|
178
219
|
### 5. Working with Actors
|
|
179
220
|
|
|
180
221
|
```python
|
|
@@ -203,10 +244,29 @@ outbox = ap.OrderedCollection(
|
|
|
203
244
|
first="https://myapp.example/user/alice/outbox?page=1",
|
|
204
245
|
)
|
|
205
246
|
|
|
206
|
-
# Library handles parsing remote collections
|
|
207
|
-
items = backend.parse_collection(url="https://example.com/user/bob/outbox")
|
|
247
|
+
# Library handles parsing remote collections (async)
|
|
248
|
+
items = await backend.parse_collection(url="https://example.com/user/bob/outbox")
|
|
249
|
+
|
|
250
|
+
# Or use sync wrapper
|
|
251
|
+
items = backend.parse_collection_sync(url="https://example.com/user/bob/outbox")
|
|
208
252
|
```
|
|
209
253
|
|
|
254
|
+
## API Naming Convention
|
|
255
|
+
|
|
256
|
+
The library uses an **async-first** naming convention:
|
|
257
|
+
|
|
258
|
+
| Operation | Async (Primary) | Sync Wrapper |
|
|
259
|
+
|-----------|----------------|--------------|
|
|
260
|
+
| Fetch IRI | `fetch_iri()` | `fetch_iri_sync()` |
|
|
261
|
+
| Fetch JSON | `fetch_json()` | `fetch_json_sync()` |
|
|
262
|
+
| Get Actor | `get_actor()` | `get_actor_sync()` |
|
|
263
|
+
| Get Object | `get_object()` | `get_object_sync()` |
|
|
264
|
+
| WebFinger | `webfinger()` | `webfinger_sync()` |
|
|
265
|
+
| Verify Signature | `verify_request()` | `verify_request_sync()` |
|
|
266
|
+
| Parse Collection | `parse_collection()` | `parse_collection_sync()` |
|
|
267
|
+
|
|
268
|
+
**Guideline:** Use async methods by default. Use `_sync()` variants only when integrating with sync frameworks like Flask or Django sync views.
|
|
269
|
+
|
|
210
270
|
## Plugin Responsibilities
|
|
211
271
|
|
|
212
272
|
| What Library Does | What Your App Does |
|