mirror of
https://git.exozy.me/a/fuwuqi.git
synced 2024-11-25 10:31:07 +00:00
Add latest working code
This commit is contained in:
parent
f463134952
commit
a2e773ebd1
16 changed files with 95 additions and 224 deletions
16
README.md
16
README.md
|
@ -2,11 +2,9 @@
|
||||||
|
|
||||||
Fuwuqi (fúwùqì or 服务器 means "server" in Chinese) is a useless C2S ActivityPub server for "extremely hardcore" ActivityPub enthusiasts. Craft your own exquisite WebFinger response! Customize your actor object exactly like you want! Hack and extend the 100-line Python server code!
|
Fuwuqi (fúwùqì or 服务器 means "server" in Chinese) is a useless C2S ActivityPub server for "extremely hardcore" ActivityPub enthusiasts. Craft your own exquisite WebFinger response! Customize your actor object exactly like you want! Hack and extend the 100-line Python server code!
|
||||||
|
|
||||||
If that sounds like a world of pain, close this webpage now... OK, got that out of the way. Time for some fun!
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
First, clone this repo on your server.
|
First, clone this repo on your server and your client device.
|
||||||
|
|
||||||
Now, generate an RSA keypair on your client device:
|
Now, generate an RSA keypair on your client device:
|
||||||
```bash
|
```bash
|
||||||
|
@ -14,22 +12,24 @@ openssl genrsa -out private.pem 2048
|
||||||
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
|
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
|
||||||
```
|
```
|
||||||
|
|
||||||
Rename `users/test.jsonld` to your username and pop it open in your favorite text editor. You should change `0.exozy.me` to match your server's domain name, `test` to your username, and the `publicKeyPem` field to the public key you just generated.
|
On the server, rename `users/test.jsonld` to your username and pop it open in your favorite text editor. You should change `0.exozy.me` to match your server's domain name, `test` to your username, and the `publicKeyPem` field to the public key you just generated.
|
||||||
|
|
||||||
Onward! Now open `.well-known/webfinger` in your editor, and modify it similarly.
|
Onward! Now open `.well-known/webfinger` in your editor, and modify it similarly. Finally, open `server.py` and update the domain, obviously.
|
||||||
|
|
||||||
That wasn't so bad, was it? (sobbing sounds)
|
That wasn't so bad, was it? (sobbing sounds)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Alright, time for the real deal. Start up `python main.py` on your server. If you want to customize the port or whatever, just change the source code.
|
Alright, time for the real deal. Start up `python server.py` on your server. If you want to customize the port or whatever, just change the source code.
|
||||||
|
|
||||||
Now on your client device, open up your favorite C2S ActivityPub client... oh wait... there aren't any! Welp, you'll just have to settle for reading the [AP spec](https://www.w3.org/TR/activitypub/), writing some homemade JSON, and figuring out how `python client.py` works.
|
Now on your client device, open up your favorite C2S ActivityPub client... oh wait... there aren't any! Welp, you'll just have to settle for reading the [AP spec](https://www.w3.org/TR/activitypub/), writing some homemade JSON, and figuring out how `python client.py` works. That's rough, buddy. You'll have to learn how to manually write `Note`s, manually accept follow requests, and more! You can find some examples in this repo. HTTP signatures are generated client-side, for no good reason other than it works. If you want to view your unread messages, just `curl` your inbox. Easy as that.
|
||||||
|
|
||||||
Like and subscribe and enjoy your new "extremely hardcore" ActivityPub server!!! 🎉😎🚀🙃🥳
|
Enjoy your new "extremely hardcore" ActivityPub server!!! 🎉😎🚀🙃🥳
|
||||||
|
|
||||||
## Resources
|
## Resources
|
||||||
|
|
||||||
- https://www.w3.org/TR/activitypub/
|
- https://www.w3.org/TR/activitypub/
|
||||||
- https://blog.joinmastodon.org/2018/06/how-to-implement-a-basic-activitypub-server/
|
- https://blog.joinmastodon.org/2018/06/how-to-implement-a-basic-activitypub-server/
|
||||||
- https://blog.joinmastodon.org/2018/07/how-to-make-friends-and-verify-requests/
|
- https://blog.joinmastodon.org/2018/07/how-to-make-friends-and-verify-requests/
|
||||||
|
- https://docs.joinmastodon.org/spec/
|
||||||
|
- https://socialhub.activitypub.rocks/t/python-mastodon-server-post-with-http-signature/2757
|
||||||
|
|
12
accept.jsonld
Normal file
12
accept.jsonld
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
"type": "Accept",
|
||||||
|
"actor": "https://0.exozy.me/users/test.jsonld",
|
||||||
|
"object": {
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
"id": "https://social.exozy.me/b363f127-6422-4566-b8f1-878aa33b0e1c",
|
||||||
|
"type": "Follow",
|
||||||
|
"actor": "https://social.exozy.me/users/a",
|
||||||
|
"object": "https://0.exozy.me/users/test.jsonld"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"@context": "https://www.w3.org/ns/activitystreams",
|
|
||||||
"id": "https://0.exozy.me/users/test.outbox/hello-world2",
|
|
||||||
"type": "Create",
|
|
||||||
"actor": "https://0.exozy.me/users/test.jsonld",
|
|
||||||
"object": {
|
|
||||||
"id": "https://0.exozy.me/users/test.statuses/hello-world2",
|
|
||||||
"type": "Note",
|
|
||||||
"attributedTo": "https://0.exozy.me/users/test.jsonld",
|
|
||||||
"inReplyTo": "https://social.exozy.me/@a/109707513227348721",
|
|
||||||
"content": "Hello from fuwuqi! 2",
|
|
||||||
"to": "https://www.w3.org/ns/activitystreams#Public"
|
|
||||||
}
|
|
||||||
}
|
|
11
client.py
11
client.py
|
@ -3,16 +3,17 @@ from cryptography.hazmat.primitives.asymmetric import padding
|
||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
from email.utils import formatdate
|
from email.utils import formatdate
|
||||||
from requests import post
|
from requests import post
|
||||||
|
from sys import argv
|
||||||
|
|
||||||
date = formatdate(usegmt=True)
|
date = formatdate(usegmt=True)
|
||||||
|
|
||||||
with open('activity.jsonld', 'rb') as f:
|
with open(argv[1], 'rb') as f:
|
||||||
activity = f.read()
|
activity = f.read()
|
||||||
|
|
||||||
digester = hashes.Hash(hashes.SHA256())
|
digester = hashes.Hash(hashes.SHA256())
|
||||||
digester.update(activity)
|
digester.update(activity)
|
||||||
digest = b64encode(digester.finalize()).decode()
|
digest = b64encode(digester.finalize()).decode()
|
||||||
message = f'(request-target): post /users/a/inbox\nhost: social.exozy.me\ndate: {date}\ndigest: SHA-256={digest}'
|
message = f'date: {date}\ndigest: SHA-256={digest}'
|
||||||
|
|
||||||
with open('private.pem', 'rb') as f:
|
with open('private.pem', 'rb') as f:
|
||||||
privkey = serialization.load_pem_private_key(f.read(), None)
|
privkey = serialization.load_pem_private_key(f.read(), None)
|
||||||
|
@ -22,11 +23,9 @@ signature = b64encode(privkey.sign(
|
||||||
padding.PKCS1v15(),
|
padding.PKCS1v15(),
|
||||||
hashes.SHA256()
|
hashes.SHA256()
|
||||||
)).decode()
|
)).decode()
|
||||||
header = f'keyId="https://0.exozy.me/users/test.jsonld#main-key",headers="(request-target) host date digest",signature="{signature}"'
|
header = f'keyId="https://0.exozy.me/users/test.jsonld#main-key",headers="date digest",signature="{signature}"'
|
||||||
|
|
||||||
resp = post('http://localhost:4200/users/test.outbox', headers={
|
resp = post('https://0.exozy.me/users/test.outbox', headers={
|
||||||
'(request-target)': 'post /users/a/inbox',
|
|
||||||
'Host': 'social.exozy.me',
|
|
||||||
'Date': date,
|
'Date': date,
|
||||||
'Digest': f'SHA-256={digest}',
|
'Digest': f'SHA-256={digest}',
|
||||||
'Signature': header,
|
'Signature': header,
|
||||||
|
|
14
create.jsonld
Normal file
14
create.jsonld
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
"id": "https://0.exozy.me/users/test.outbox/hello-world2",
|
||||||
|
"type": "Create",
|
||||||
|
"actor": "https://0.exozy.me/users/test.jsonld",
|
||||||
|
"object": {
|
||||||
|
"id": "https://0.exozy.me/users/test.statuses/hello-world2",
|
||||||
|
"type": "Note",
|
||||||
|
"attributedTo": "https://0.exozy.me/users/test.jsonld",
|
||||||
|
"inReplyTo": "https://social.exozy.me/@a/109707513227348721",
|
||||||
|
"content": "Hello from fuwuqi! 2",
|
||||||
|
"to": "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
}
|
||||||
|
}
|
7
follow.jsonld
Normal file
7
follow.jsonld
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
"id": "https://0.exozy.me/users/test.outbox/follow",
|
||||||
|
"type": "Follow",
|
||||||
|
"actor": "https://0.exozy.me/users/test.jsonld",
|
||||||
|
"object": "https://social.exozy.me/users/a"
|
||||||
|
}
|
28
private.pem
28
private.pem
|
@ -1,28 +0,0 @@
|
||||||
-----BEGIN PRIVATE KEY-----
|
|
||||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCc+fpJFDUlPCoU
|
|
||||||
wUmrsGDExb/hX8dhKMc9u0ov/ePBc5OY4tTPjCvghvUjCIqmyC7PS7JfUuNt7epD
|
|
||||||
OpoJm7mF2gaj64mxqfPrICgtjARN8gUiOpCvcWggZDFgkMQuSFI7yG1s4oqhGJUq
|
|
||||||
yMPnHhBwSRmQsBp5/xZWHTywSoTd8Li4uY6VJq/JxswuZqH1R2GWMVxpVJDuqjH0
|
|
||||||
cW8oXZ6LgGY3ZZ0wZnwk3bSdzQ7GhmkGkwCtGoXSSuMiSEDn/0K8hkRQs9AKFgvo
|
|
||||||
ByYtLSP/JPETzZ4UwEG/J6DwgIxHNxsKg36FWe/dxv/v4HA12hCNmq3REb+GFulz
|
|
||||||
xEq35g71AgMBAAECggEACqnGcS67ZMyfSn/4xP4QYhLRc89MSBLQ5KYeJirCd70l
|
|
||||||
97riMY5i2qKARhbJ8wYNJqK3OqzIQIrqoLzQguTo/NPbI0kYNjvbvbXrqhsP8yAv
|
|
||||||
bhb5W27s36ppWkHAjyj1gRRz2R4obu9bDqggT/Olso2cpsyiTSA2qwyFt1n7MueQ
|
|
||||||
ag/lSYAAnjRfnxnGiBwy+wURxZH+Y9uBhb9E8MZ+W+sUc7l8RDUfYPN9d7NmxfdV
|
|
||||||
x6x+jk+OLpYotj1v6aTfdwBCqpdt2ulMZexTCfjwAIU/XI2uhqaO1dzKI+Ssr/WY
|
|
||||||
j8R7LXV94pI1ZXdwAJ/8aWGllAhj1hpnhacjnr7JAQKBgQDZPj5zJVk7TBoRxQVw
|
|
||||||
VCfmGwPwAkfmGGqn9g41VlKtyG+JhWQ7aAs0gMFwq6QGDEBloAiXQw7z033ZSNTP
|
|
||||||
p61QCePq6fLGEgdsfgjGotEbZ2XDZdB6fdzK+sKJLQtXr8FVrywyNmlX0bk8IRSs
|
|
||||||
hnJXYco/VTLeDiQePO2bjm0UdQKBgQC4+0hR/se6AHgN9449ruHLSOCcC+Y6WQJ1
|
|
||||||
BM+ydk7OnnfDmxzXQMbzri5kwDTBw+Y01avbRkpMZbHtWip9QWL96Kf8D6OUoX6d
|
|
||||||
KJuheKsuJUKmXTPF8YyJiY+KXmx1yz7hu8X/SN955s4BAttIZGQvSIFpy+izzQWc
|
|
||||||
FghcplvAgQKBgQCIkuIN37AOYFSPUU6PBMkkl11NWRG8bSM4Pq9GBuPpjvXX/f06
|
|
||||||
f7lzo3J5E98FUlR1zzs3ZRgUX6RhorDvb1m81Mrtl3Bh51m1cjKwNhHB6aoHQo3j
|
|
||||||
RBc3oJgGR0Q3Ny4TYRIm6yAk7ptGWwG1SLy/hKHyWOymvzsjq2gxgEPBNQKBgEyM
|
|
||||||
6KvOBPdLVGNrS/jo01Yd7Z2GKxuAVEz61bzjys8ksylGmpPVob+cGGTnSa3aFP1O
|
|
||||||
Y1VV7E9bUluIEcdN9NpgmovsKOTMRCpjcKxM1II/NyrDrTZANMmCHN3FH5tLpdUi
|
|
||||||
sNhpXtoCksPGW9rEeNU8axnOIZmuwaCLWaCF07iBAoGAX494Slca2EnERTtcX9jq
|
|
||||||
SM/srMTz+cm5zNA3npMj1eZ7zNglD5tZapc3f5OErrDdZz5wlmIo/eheQxb8TMQW
|
|
||||||
F5goYHwpq6bghHlbAxK2I353s8Q8WriLTvAYouHfEqd14AS42OXD44CBwU0V5rRV
|
|
||||||
MmlfOVBGqPVHLQK/tOebZIw=
|
|
||||||
-----END PRIVATE KEY-----
|
|
|
@ -1,9 +0,0 @@
|
||||||
-----BEGIN PUBLIC KEY-----
|
|
||||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnPn6SRQ1JTwqFMFJq7Bg
|
|
||||||
xMW/4V/HYSjHPbtKL/3jwXOTmOLUz4wr4Ib1IwiKpsguz0uyX1Ljbe3qQzqaCZu5
|
|
||||||
hdoGo+uJsanz6yAoLYwETfIFIjqQr3FoIGQxYJDELkhSO8htbOKKoRiVKsjD5x4Q
|
|
||||||
cEkZkLAaef8WVh08sEqE3fC4uLmOlSavycbMLmah9UdhljFcaVSQ7qox9HFvKF2e
|
|
||||||
i4BmN2WdMGZ8JN20nc0OxoZpBpMArRqF0krjIkhA5/9CvIZEULPQChYL6AcmLS0j
|
|
||||||
/yTxE82eFMBBvyeg8ICMRzcbCoN+hVnv3cb/7+BwNdoQjZqt0RG/hhbpc8RKt+YO
|
|
||||||
9QIDAQAB
|
|
||||||
-----END PUBLIC KEY-----
|
|
53
server.py
53
server.py
|
@ -9,6 +9,9 @@ from os.path import isfile
|
||||||
from urllib.parse import quote_plus
|
from urllib.parse import quote_plus
|
||||||
|
|
||||||
|
|
||||||
|
domain = 'https://0.exozy.me'
|
||||||
|
|
||||||
|
|
||||||
def collection_append(file, item):
|
def collection_append(file, item):
|
||||||
with open(file) as f:
|
with open(file) as f:
|
||||||
collection = load(f)
|
collection = load(f)
|
||||||
|
@ -18,6 +21,20 @@ def collection_append(file, item):
|
||||||
dump(collection, f)
|
dump(collection, f)
|
||||||
|
|
||||||
|
|
||||||
|
def iri_to_actor(iri):
|
||||||
|
if domain in iri:
|
||||||
|
name = search(f'^{domain}/users/(.*?)#main-key$', iri).group(1)
|
||||||
|
actorfile = f'users/{name}'
|
||||||
|
else:
|
||||||
|
actorfile = f'users/{quote_plus(iri)}'
|
||||||
|
if not isfile(actorfile):
|
||||||
|
with open(actorfile, 'w') as f:
|
||||||
|
resp = get(iri, headers={'Accept': 'application/activity+json'})
|
||||||
|
f.write(resp.text)
|
||||||
|
with open(actorfile) as f:
|
||||||
|
return load(f)
|
||||||
|
|
||||||
|
|
||||||
class fuwuqi(SimpleHTTPRequestHandler):
|
class fuwuqi(SimpleHTTPRequestHandler):
|
||||||
def do_POST(self):
|
def do_POST(self):
|
||||||
body = self.rfile.read(int(self.headers['Content-Length']))
|
body = self.rfile.read(int(self.headers['Content-Length']))
|
||||||
|
@ -26,20 +43,21 @@ class fuwuqi(SimpleHTTPRequestHandler):
|
||||||
print(self.headers)
|
print(self.headers)
|
||||||
print(self.path)
|
print(self.path)
|
||||||
|
|
||||||
|
username = search('^/users/(.*)\.(in|out)box$', self.path).group(1)
|
||||||
|
|
||||||
# Get actor public key
|
# Get actor public key
|
||||||
keyid = search('keyId="(.*?)"', self.headers['Signature']).group(1)
|
keyid = search('keyId="(.*?)"', self.headers['Signature']).group(1)
|
||||||
actorfile = f'users/{quote_plus(keyid)}'
|
actor = iri_to_actor(keyid)
|
||||||
if not isfile(actorfile):
|
pubkeypem = actor['publicKey']['publicKeyPem'].encode('utf8')
|
||||||
with open(actorfile, 'w') as f:
|
|
||||||
f.write(get(keyid).text)
|
|
||||||
with open(actorfile) as f:
|
|
||||||
pubkeypem = load(f)['publicKey']['publicKeyPem'].encode('utf8')
|
|
||||||
pubkey = serialization.load_pem_public_key(pubkeypem, None)
|
pubkey = serialization.load_pem_public_key(pubkeypem, None)
|
||||||
|
|
||||||
# Assemble headers
|
# Assemble headers
|
||||||
headers = search('headers="(.*?)"', self.headers['Signature']).group(1)
|
headers = search('headers="(.*?)"', self.headers['Signature']).group(1)
|
||||||
message = ''
|
message = ''
|
||||||
for header in headers.split():
|
for header in headers.split():
|
||||||
|
if header == '(request-target)':
|
||||||
|
headerval = f'post {self.path}'
|
||||||
|
else:
|
||||||
headerval = self.headers[header]
|
headerval = self.headers[header]
|
||||||
message += f'{header}: {headerval}\n'
|
message += f'{header}: {headerval}\n'
|
||||||
|
|
||||||
|
@ -61,27 +79,34 @@ class fuwuqi(SimpleHTTPRequestHandler):
|
||||||
self.send_response(401)
|
self.send_response(401)
|
||||||
return
|
return
|
||||||
|
|
||||||
username = search('^/users/(.*)\.(in|out)box$', self.path).group(1)
|
|
||||||
if self.path.endswith('inbox'):
|
if self.path.endswith('inbox'):
|
||||||
# S2S
|
# S2S
|
||||||
id = activity['id'].split('/')[-1]
|
collection_append(f'users/{username}.inbox', activity)
|
||||||
with open(f'users/{username}/{id}', 'w') as f:
|
|
||||||
dump(activity, f)
|
|
||||||
elif self.path.endswith('outbox'):
|
elif self.path.endswith('outbox'):
|
||||||
# C2S
|
# C2S
|
||||||
collection_append(f'users/{username}.outbox', activity)
|
collection_append(f'users/{username}.outbox', activity)
|
||||||
|
|
||||||
if activity['type'] == 'Create':
|
if activity['type'] == 'Create':
|
||||||
id = activity['id'].split('/')[-1]
|
id = activity['id'].split('/')[-1]
|
||||||
with open(f'users/{username}.statuses/{id}', 'w') as f:
|
with open(f'users/{username}.statuses/{id}', 'w') as f:
|
||||||
dump(activity['object'], f)
|
dump(activity['object'], f)
|
||||||
print(self.headers)
|
# Send to followers
|
||||||
print(body)
|
with open(f'users/{username}.following') as f:
|
||||||
resp = post('https://social.exozy.me/users/a/inbox', headers=self.headers, data=body)
|
for followed in load(f)['orderedItems']:
|
||||||
|
actor = iri_to_actor(followed)
|
||||||
|
self.headers['Host'] = followed.split('/')[2]
|
||||||
|
resp = post(actor['inbox'], headers=self.headers, data=body)
|
||||||
print(resp)
|
print(resp)
|
||||||
print(resp.text)
|
print(resp.text)
|
||||||
|
elif activity['type'] == 'Follow':
|
||||||
|
object = iri_to_actor(activity['object'])
|
||||||
|
self.headers['Host'] = activity['object'].split('/')[2]
|
||||||
|
resp = post(object['inbox'], headers=self.headers, data=body)
|
||||||
|
print(resp)
|
||||||
|
print(resp.text)
|
||||||
|
collection_append(f'users/{username}.following', activity['object'])
|
||||||
|
|
||||||
self.send_response(200)
|
self.send_response(200)
|
||||||
|
self.end_headers()
|
||||||
|
|
||||||
|
|
||||||
ThreadingHTTPServer(('localhost', 4200), fuwuqi).serve_forever()
|
ThreadingHTTPServer(('localhost', 4200), fuwuqi).serve_forever()
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
{
|
|
||||||
"@context": [
|
|
||||||
"https://www.w3.org/ns/activitystreams",
|
|
||||||
"https://w3id.org/security/v1"
|
|
||||||
],
|
|
||||||
"id": "https://0.exozy.me/users/test.jsonld",
|
|
||||||
"type": "Person",
|
|
||||||
"preferredUsername": "test",
|
|
||||||
"name": "Billiam Wender",
|
|
||||||
"inbox": "https://0.exozy.me/users/test.inbox",
|
|
||||||
"outbox": "https://0.exozy.me/users/test.outbox",
|
|
||||||
"followers": "https://0.exozy.me/users/test.followers",
|
|
||||||
"following": "https://0.exozy.me/users/test.following",
|
|
||||||
"icon": {
|
|
||||||
"type": "Image",
|
|
||||||
"mediaType": "image/png",
|
|
||||||
"url": "https://0.exozy.me/users/test.png"
|
|
||||||
},
|
|
||||||
"publicKey": {
|
|
||||||
"id": "https://0.exozy.me/users/test.jsonld#main-key",
|
|
||||||
"owner": "https://0.exozy.me/users/test.jsonld",
|
|
||||||
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnPn6SRQ1JTwqFMFJq7Bg\nxMW/4V/HYSjHPbtKL/3jwXOTmOLUz4wr4Ib1IwiKpsguz0uyX1Ljbe3qQzqaCZu5\nhdoGo+uJsanz6yAoLYwETfIFIjqQr3FoIGQxYJDELkhSO8htbOKKoRiVKsjD5x4Q\ncEkZkLAaef8WVh08sEqE3fC4uLmOlSavycbMLmah9UdhljFcaVSQ7qox9HFvKF2e\ni4BmN2WdMGZ8JN20nc0OxoZpBpMArRqF0krjIkhA5/9CvIZEULPQChYL6AcmLS0j\n/yTxE82eFMBBvyeg8ICMRzcbCoN+hVnv3cb/7+BwNdoQjZqt0RG/hhbpc8RKt+YO\n9QIDAQAB\n-----END PUBLIC KEY-----\n"
|
|
||||||
}
|
|
||||||
}
|
|
1
users/https%3A%2F%2Fsocial.exozy.me%2Fusers%2Fa
Normal file
1
users/https%3A%2F%2Fsocial.exozy.me%2Fusers%2Fa
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"manuallyApprovesFollowers":"as:manuallyApprovesFollowers","toot":"http://joinmastodon.org/ns#","featured":{"@id":"toot:featured","@type":"@id"},"featuredTags":{"@id":"toot:featuredTags","@type":"@id"},"alsoKnownAs":{"@id":"as:alsoKnownAs","@type":"@id"},"movedTo":{"@id":"as:movedTo","@type":"@id"},"schema":"http://schema.org#","PropertyValue":"schema:PropertyValue","value":"schema:value","discoverable":"toot:discoverable","Device":"toot:Device","Ed25519Signature":"toot:Ed25519Signature","Ed25519Key":"toot:Ed25519Key","Curve25519Key":"toot:Curve25519Key","EncryptedMessage":"toot:EncryptedMessage","publicKeyBase64":"toot:publicKeyBase64","deviceId":"toot:deviceId","claim":{"@type":"@id","@id":"toot:claim"},"fingerprintKey":{"@type":"@id","@id":"toot:fingerprintKey"},"identityKey":{"@type":"@id","@id":"toot:identityKey"},"devices":{"@type":"@id","@id":"toot:devices"},"messageFranking":"toot:messageFranking","messageType":"toot:messageType","cipherText":"toot:cipherText","suspended":"toot:suspended","focalPoint":{"@container":"@list","@id":"toot:focalPoint"}}],"id":"https://social.exozy.me/users/a","type":"Person","following":"https://social.exozy.me/users/a/following","followers":"https://social.exozy.me/users/a/followers","inbox":"https://social.exozy.me/users/a/inbox","outbox":"https://social.exozy.me/users/a/outbox","featured":"https://social.exozy.me/users/a/collections/featured","featuredTags":"https://social.exozy.me/users/a/collections/tags","preferredUsername":"a","name":"","summary":"\u003cp\u003e(+ (My username) (Ordinal n) (! off) (Element 39) (Wide area network) (Prefix for billion))\u003c/p\u003e\u003cp\u003eBoosts interesting things and sometimes transcribes special patterns generated by my biological neural networks into UTF-8 encoded strings.\u003c/p\u003e\u003cp\u003eCurrently oscillating between hacking ActivityPub federation into Forgejo/Gitea, pondering about the ForgeFed spec, wasting time, and sleeping (with 1/3 probability!).\u003c/p\u003e\u003cp\u003eWill happily answer any questions about forge federation.\u003c/p\u003e","url":"https://social.exozy.me/@a","manuallyApprovesFollowers":false,"discoverable":true,"published":"2022-09-07T00:00:00Z","devices":"https://social.exozy.me/users/a/collections/devices","alsoKnownAs":["https://social.exozy.me/users/ta180m"],"publicKey":{"id":"https://social.exozy.me/users/a#main-key","owner":"https://social.exozy.me/users/a","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxrHXYuP4RKjac7h0woPA\nmBeETGogZ2IPUbDhA4DliUUHbUGYIK3XeIA4iywpnTbuHxE7L2PqMYNvfYMjQ+HS\nlJfSgYLB5mFxvzNcKqaUfAZazx3RzHLxftxt4DxWhEtS+vVd4RsxU2uvCOU8nN7o\nvk40GaYrgAms/7sapAjqbn6ngclVtVOBm0QhCG9cfg4QZoIIp98wpj/7kWZHxl8Y\nKJKN4G66FP+WAPIiLO/EmBqB9jcTQM2UMIGol4+616zFbmrow4KFCxZhiap+doP2\n8SjZSlA4Fhlk4qtHvQRjPlx2u/gvyUc7gQoa3PTb64rdDw1ahGvLTQKB6uVAXpDN\n2wIDAQAB\n-----END PUBLIC KEY-----\n"},"tag":[],"attachment":[{"type":"PropertyValue","name":"Website","value":"\u003ca href=\"https://a.exozy.me\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003ea.exozy.me\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e"},{"type":"PropertyValue","name":"Code","value":"\u003ca href=\"https://git.exozy.me/a\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003egit.exozy.me/a\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e"},{"type":"PropertyValue","name":"exozyme","value":"\u003ca href=\"https://exozy.me\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003eexozy.me\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e"},{"type":"PropertyValue","name":"Also me","value":"\u003cspan class=\"h-card\"\u003e\u003ca href=\"https://mastodon.mit.edu/@xy\" class=\"u-url mention\"\u003e@\u003cspan\u003exy@mastodon.mit.edu\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e"}],"endpoints":{"sharedInbox":"https://social.exozy.me/inbox"},"icon":{"type":"Image","mediaType":"image/gif","url":"https://social.exozy.me/system/accounts/avatars/108/958/469/936/988/491/original/c16834437aa85a2a.gif"},"image":{"type":"Image","mediaType":"image/jpeg","url":"https://social.exozy.me/system/accounts/headers/108/958/469/936/988/491/original/521f64b30b55553d.jpg"}}
|
File diff suppressed because one or more lines are too long
|
@ -1,6 +1 @@
|
||||||
{
|
{"@context": "https://www.w3.org/ns/activitystreams", "type": "OrderedCollection", "totalItems": 3, "orderedItems": ["https://social.exozy.me/users/a", "https://social.exozy.me/users/a", "https://social.exozy.me/users/a"]}
|
||||||
"@context": "https://www.w3.org/ns/activitystreams",
|
|
||||||
"type": "OrderedCollection",
|
|
||||||
"totalItems": 0,
|
|
||||||
"orderedItems": [],
|
|
||||||
}
|
|
|
@ -1,6 +1 @@
|
||||||
{
|
{"@context": "https://www.w3.org/ns/activitystreams", "type": "OrderedCollection", "totalItems": 2, "orderedItems": ["https://social.exozy.me/users/a", "https://social.exozy.me/users/a"]}
|
||||||
"@context": "https://www.w3.org/ns/activitystreams",
|
|
||||||
"type": "OrderedCollection",
|
|
||||||
"totalItems": 0,
|
|
||||||
"orderedItems": [],
|
|
||||||
}
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue