Python implementation of Bluesky PDS and AT Protocol, including repo, MST, and sync methods
Project description
arroba
Python implementation of Bluesky PDS and AT Protocol, including data repository, Merkle search tree, and com.atproto.sync XRPC methods.
You can build your own PDS on top of arroba with just a few lines of Python and run it in any WSGI server. You can build a more involved PDS with custom logic and behavior. Or you can build a different ATProto service, eg an AppView, relay (née BGS), or something entirely new!
Install from PyPI with pip install arroba
.
Arroba is the Spanish word for the @ character ("at sign").
License: This project is placed in the public domain. You may also use it under the CC0 License.
Usage
Here's minimal example code for a multi-repo PDS on top of arroba and Flask:
from flask import Flask
from google.cloud import ndb
from lexrpc.flask_server import init_flask
from arroba import server
from arroba.datastore_storage import DatastoreStorage
from arroba.xrpc_sync import send_new_commits
server.storage = DatastoreStorage()
server.repo.callback = lambda _: send_new_commits() # to subscribeRepos
app = Flask('my-pds')
init_flask(server.server, app)
# for Google Cloud Datastore
ndb_client = ndb.Client()
def ndb_context_middleware(wsgi_app):
def wrapper(environ, start_response):
with ndb_client.context():
return wsgi_app(environ, start_response)
return wrapper
app.wsgi_app = ndb_context_middleware(app.wsgi_app)
See app.py
for a more comprehensive example, including a CORS handler for OPTIONS
preflight requests and a catch-all app.bsky.*
XRPC handler that proxies requests to the AppView.
Overview
Arroba consists of these parts:
- Data structures:
- Storage:
Storage
abstract base classDatastoreStorage
(uses Google Cloud Datastore)- TODO: filesystem storage
- XRPC handlers:
- Utilities:
did
: create and resolvedid:plc
s,did:web
s, and domain handlesdiff
: find the deterministic minimal difference between twoMST
sutil
: miscellaneous utilities for TIDs, AT URIs, signing and verifying signatures, generating JWTs, encoding/decoding, and more
Configuration
Configure arroba with these environment variables:
APPVIEW_HOST
, defaultapi.bsky-sandbox.dev
RELAY_HOST
, defaultbgs.bsky-sandbox.dev
PLC_HOST
, defaultplc.bsky-sandbox.dev
PDS_HOST
, where you're running your PDS
Optional, only used in com.atproto.repo and com.atproto.server XRPC handlers:
REPO_TOKEN
, static token to use as bothaccessJwt
andrefreshJwt
, defaults to contents ofrepo_token
file. Not required to be an actual JWT. If not set, XRPC methods that require auth will return HTTP 501 Not Implemented.
Changelog
0.5 - 2024-03-16
- Bug fix: base32-encode TIDs in record keys,
at://
URIs, commitrev
s, etc. Before, we were using the integer UNIX timestamp directly, which happened to be the same 13 character length. Oops. - Switch from
BGS_HOST
environment variable toRELAY_HOST
.BGS_HOST
is still supported for backward compatibility. datastore_storage
:- Bug fix for
DatastoreStorage.last_seq
, handle new NSID. - Add new
AtpRemoteBlob
class for storing "remote" blobs, available at public HTTP URLs, that we don't store ourselves.
- Bug fix for
did
:create_plc
: strip padding from genesis operation signature (for did-method-plc#54, atproto#1839).resolve_handle
: return None on bad domain, eg.foo.com
.resolve_handle
bug fix: handlecharset
specifier in HTTPS method responseContent-Type
.
util
:new_key
: addseed
kwarg to allow deterministic key generation.
xrpc_repo
:getRecord
: try to load record locally first; if not available, forward to AppView.
xrpc_sync
:- Implement
getBlob
, right now only based on "remote" blobs stored inAtpRemoteBlob
s in datastore storage.
- Implement
0.4 - 2023-09-19
- Migrate to ATProto repo v3. Specifically, the existing
subscribeRepos
sequence number is reused as the newrev
field in commits. (Discussion.). - Add new
did
module with utilities to create and resolvedid:plc
s and resolvedid:web
s. - Add new
util.service_jwt
function that generates ATProto inter-service JWTs. Repo
:- Add new
signing_key
/rotation_key
attributes. Generate store, and load both indatastore_storage
. - Remove
format_init_commit
, migrate existing calls toformat_commit
.
- Add new
Storage
:- Rename
read_from_seq
=>read_blocks_by_seq
(and inMemoryStorage
andDatastoreStorage
), add newread_commits_by_seq
method. - Merge
load_repo
did
/handle
kwargs intodid_or_handle
.
- Rename
- XRPCs:
- Make
subscribeRepos
check storage for all new commits every time it wakes up.- As part of this, replace
xrpc_sync.enqueue_commit
with newsend_new_commits
function that takes no parameters.
- As part of this, replace
- Drop bundled
app.bsky
/com.atproto
lexicons, use lexrpc's instead.
- Make
0.3 - 2023-08-29
Big milestone: arroba is successfully federating with the ATProto sandbox! See app.py for the minimal demo code needed to wrap arroba in a fully functional PDS.
- Add Google Cloud Datastore implementation of repo storage.
- Implement
com.atproto
XRPC methods needed to federate with sandbox, including most ofrepo
andsync
.- Notably, includes
subscribeRepos
server side over websocket.
- Notably, includes
- ...and much more.
0.2 - 2023-05-18
Implement repo and commit chain in new Repo class, including pluggable storage. This completes the first pass at all PDS data structures. Next release will include initial implementations of the com.atproto.sync.*
XRPC methods.
0.1 - 2023-04-30
Initial release! Still very in progress. MST, Walker, and Diff classes are mostly complete and working. Repo, commits, and sync XRPC methods are still in progress.
Release instructions
Here's how to package, test, and ship a new release.
-
Run the unit tests.
source local/bin/activate.csh python3 -m unittest discover
-
Bump the version number in
pyproject.toml
anddocs/conf.py
.git grep
the old version number to make sure it only appears in the changelog. Change the current changelog entry inREADME.md
for this new version from unreleased to the current date. -
Build the docs. If you added any new modules, add them to the appropriate file(s) in
docs/source/
. Then run./docs/build.sh
. Check that the generated HTML looks fine by openingdocs/_build/html/index.html
and looking around. -
setenv ver X.Y git commit -am "release v$ver"
-
Upload to test.pypi.org for testing.
python3 -m build twine upload -r pypitest dist/arroba-$ver*
-
Install from test.pypi.org.
cd /tmp python3 -m venv local source local/bin/activate.csh # make sure we force pip to use the uploaded version pip3 uninstall arroba pip3 install --upgrade pip pip3 install -i https://test.pypi.org/simple --extra-index-url https://pypi.org/simple arroba==$ver deactivate
-
Smoke test that the code trivially loads and runs.
source local/bin/activate.csh python3 # TODO: test code deactivate
-
Tag the release in git. In the tag message editor, delete the generated comments at bottom, leave the first line blank (to omit the release "title" in github), put
### Notable changes
on the second line, then copy and paste this version's changelog contents below it.git tag -a v$ver --cleanup=verbatim git push && git push --tags
-
Click here to draft a new release on GitHub. Enter
vX.Y
in the Tag version box. Leave Release title empty. Copy### Notable changes
and the changelog contents into the description text box. -
Upload to pypi.org!
twine upload dist/arroba-$ver*
-
Wait for the docs to build on Read the Docs, then check that they look ok.
-
On the Versions page, check that the new version is active, If it's not, activate it in the Activate a Version section.
Project details
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
File details
Details for the file arroba-0.5.tar.gz
.
File metadata
- Download URL: arroba-0.5.tar.gz
- Upload date:
- Size: 42.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.0.0 CPython/3.12.2
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 6b8cb8e9032588f23b2006157d7ad739e75202b8f2255a8cdae5043a93476dae |
|
MD5 | b2ed9cd4fdfd8b3254a77768fdb89ac7 |
|
BLAKE2b-256 | 5e0fdd5087f98d736906c4498d93c5c2ce84a9af1d7788e2a422b2ad97a134eb |
File details
Details for the file arroba-0.5-py3-none-any.whl
.
File metadata
- Download URL: arroba-0.5-py3-none-any.whl
- Upload date:
- Size: 43.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.0.0 CPython/3.12.2
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | a920b3094c28dd140730a240bf822309a30c94c40876caf30c0d04cdea21439a |
|
MD5 | ffc9b4b9d44b14ae987cdbc366d6fd09 |
|
BLAKE2b-256 | e100e50515c81c3896f817190e2712cf03c5777340f378e4f308ed4e07628018 |