From 53f0c72117894c389e6e4c2990f802486eff330e Mon Sep 17 00:00:00 2001 From: Anthony Wang Date: Wed, 18 Jan 2023 05:01:59 +0000 Subject: Implement (broken) HTTP signatures --- README.md | 2 +- activity.jsonld | 14 ++++ client.py | 35 ++++++++ main.py | 10 --- private.pem | 28 +++++++ public.pem | 9 ++ server.py | 87 +++++++++++++++++++ ...F%2F0.exozy.me%2Fusers%2Ftest.jsonld%23main-key | 24 ++++++ ...%3A%2F%2Fsocial.exozy.me%2Fusers%2Fa%23main-key | 98 ++++++++++++++++++++++ users/test.followers | 6 ++ users/test.following | 6 ++ users/test.inbox | 6 ++ users/test.jsonld | 2 + users/test.outbox | 1 + users/test.statuses/hello-world | 1 + users/test.statuses/hello-world2 | 1 + 16 files changed, 319 insertions(+), 11 deletions(-) create mode 100644 activity.jsonld create mode 100644 client.py delete mode 100644 main.py create mode 100644 private.pem create mode 100644 public.pem create mode 100644 server.py create mode 100644 users/https%3A%2F%2F0.exozy.me%2Fusers%2Ftest.jsonld%23main-key create mode 100644 users/https%3A%2F%2Fsocial.exozy.me%2Fusers%2Fa%23main-key create mode 100644 users/test.followers create mode 100644 users/test.following create mode 100644 users/test.inbox create mode 100644 users/test.outbox create mode 100644 users/test.statuses/hello-world create mode 100644 users/test.statuses/hello-world2 diff --git a/README.md b/README.md index 06c228d..b70d965 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ That wasn't so bad, was it? (sobbing sounds) 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. -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/) and `curl`ing some homemade JSON. That's rough, buddy. +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. Like and subscribe and enjoy your new "extremely hardcore" ActivityPub server!!! 🎉😎🚀🙃🥳 diff --git a/activity.jsonld b/activity.jsonld new file mode 100644 index 0000000..ab5837d --- /dev/null +++ b/activity.jsonld @@ -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" + } +} diff --git a/client.py b/client.py new file mode 100644 index 0000000..8cf4281 --- /dev/null +++ b/client.py @@ -0,0 +1,35 @@ +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import padding +from base64 import b64encode +from email.utils import formatdate +from requests import post + +date = formatdate(usegmt=True) + +with open('activity.jsonld', 'rb') as f: + activity = f.read() + +digester = hashes.Hash(hashes.SHA256()) +digester.update(activity) +digest = b64encode(digester.finalize()).decode() +message = f'(request-target): post /users/a/inbox\nhost: social.exozy.me\ndate: {date}\ndigest: SHA-256={digest}' + +with open('private.pem', 'rb') as f: + privkey = serialization.load_pem_private_key(f.read(), None) + +signature = b64encode(privkey.sign( + message.encode('utf8'), + padding.PKCS1v15(), + hashes.SHA256() +)).decode() +header = f'keyId="https://0.exozy.me/users/test.jsonld#main-key",headers="(request-target) host date digest",signature="{signature}"' + +resp = post('http://localhost:4200/users/test.outbox', headers={ + '(request-target)': 'post /users/a/inbox', + 'Host': 'social.exozy.me', + 'Date': date, + 'Digest': f'SHA-256={digest}', + 'Signature': header, +}, data=activity) +print(resp) +print(resp.text) diff --git a/main.py b/main.py deleted file mode 100644 index 5ef7f9b..0000000 --- a/main.py +++ /dev/null @@ -1,10 +0,0 @@ -from http.server import SimpleHTTPRequestHandler, HTTPServer - -class fuwuqi(SimpleHTTPRequestHandler): - def do_POST(self): - self.send_response(200) - self.send_header('Content-type', 'text/html') - self.end_headers() - self.wfile.write('hi!'.encode('utf-8')) - -HTTPServer(('localhost', 4200), fuwuqi).serve_forever() diff --git a/private.pem b/private.pem new file mode 100644 index 0000000..93c0375 --- /dev/null +++ b/private.pem @@ -0,0 +1,28 @@ +-----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----- diff --git a/public.pem b/public.pem new file mode 100644 index 0000000..d8c25bf --- /dev/null +++ b/public.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnPn6SRQ1JTwqFMFJq7Bg +xMW/4V/HYSjHPbtKL/3jwXOTmOLUz4wr4Ib1IwiKpsguz0uyX1Ljbe3qQzqaCZu5 +hdoGo+uJsanz6yAoLYwETfIFIjqQr3FoIGQxYJDELkhSO8htbOKKoRiVKsjD5x4Q +cEkZkLAaef8WVh08sEqE3fC4uLmOlSavycbMLmah9UdhljFcaVSQ7qox9HFvKF2e +i4BmN2WdMGZ8JN20nc0OxoZpBpMArRqF0krjIkhA5/9CvIZEULPQChYL6AcmLS0j +/yTxE82eFMBBvyeg8ICMRzcbCoN+hVnv3cb/7+BwNdoQjZqt0RG/hhbpc8RKt+YO +9QIDAQAB +-----END PUBLIC KEY----- diff --git a/server.py b/server.py new file mode 100644 index 0000000..a68efc9 --- /dev/null +++ b/server.py @@ -0,0 +1,87 @@ +from base64 import b64decode +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import padding +from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer +from json import dump, load, loads +from re import search +from requests import get, post +from os.path import isfile +from urllib.parse import quote_plus + + +def collection_append(file, item): + with open(file) as f: + collection = load(f) + collection['totalItems'] += 1 + collection['orderedItems'].append(item) + with open(file, 'w') as f: + dump(collection, f) + + +class fuwuqi(SimpleHTTPRequestHandler): + def do_POST(self): + body = self.rfile.read(int(self.headers['Content-Length'])) + activity = loads(body) + print(activity) + print(self.headers) + print(self.path) + + # Get actor public key + keyid = search('keyId="(.*?)"', self.headers['Signature']).group(1) + actorfile = f'users/{quote_plus(keyid)}' + if not isfile(actorfile): + 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) + + # Assemble headers + headers = search('headers="(.*?)"', self.headers['Signature']).group(1) + message = '' + for header in headers.split(): + headerval = self.headers[header] + message += f'{header}: {headerval}\n' + + # Verify HTTP signature + signature = search('signature="(.*?)"', self.headers['Signature']).group(1) + pubkey.verify( + b64decode(signature), + message[:-1].encode('utf8'), + padding.PKCS1v15(), + hashes.SHA256() + ) + + # Make sure activity doer matches HTTP signature + actor = keyid.removesuffix('#main-key') + if 'actor' in activity and activity['actor'] != actor: + self.send_response(401) + return + if 'attributedTo' in activity and activity['attributedTo'] != actor: + self.send_response(401) + return + + username = search('^/users/(.*)\.(in|out)box$', self.path).group(1) + if self.path.endswith('inbox'): + # S2S + id = activity['id'].split('/')[-1] + with open(f'users/{username}/{id}', 'w') as f: + dump(activity, f) + elif self.path.endswith('outbox'): + # C2S + collection_append(f'users/{username}.outbox', activity) + + if activity['type'] == 'Create': + id = activity['id'].split('/')[-1] + with open(f'users/{username}.statuses/{id}', 'w') as f: + dump(activity['object'], f) + print(self.headers) + print(body) + resp = post('https://social.exozy.me/inbox', headers=self.headers, data=body) + print(resp) + print(resp.text) + + self.send_response(200) + + +ThreadingHTTPServer(('localhost', 4200), fuwuqi).serve_forever() diff --git a/users/https%3A%2F%2F0.exozy.me%2Fusers%2Ftest.jsonld%23main-key b/users/https%3A%2F%2F0.exozy.me%2Fusers%2Ftest.jsonld%23main-key new file mode 100644 index 0000000..82cccc2 --- /dev/null +++ b/users/https%3A%2F%2F0.exozy.me%2Fusers%2Ftest.jsonld%23main-key @@ -0,0 +1,24 @@ +{ + "@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" + } +} diff --git a/users/https%3A%2F%2Fsocial.exozy.me%2Fusers%2Fa%23main-key b/users/https%3A%2F%2Fsocial.exozy.me%2Fusers%2Fa%23main-key new file mode 100644 index 0000000..6fe1753 --- /dev/null +++ b/users/https%3A%2F%2Fsocial.exozy.me%2Fusers%2Fa%23main-key @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + +​ (@a@exozy.me) - exocial + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+ + + + + + + + + + + + + + + +
+ + diff --git a/users/test.followers b/users/test.followers new file mode 100644 index 0000000..a11f198 --- /dev/null +++ b/users/test.followers @@ -0,0 +1,6 @@ +{ + "@context": "https://www.w3.org/ns/activitystreams", + "type": "OrderedCollection", + "totalItems": 0, + "orderedItems": [], +} diff --git a/users/test.following b/users/test.following new file mode 100644 index 0000000..a11f198 --- /dev/null +++ b/users/test.following @@ -0,0 +1,6 @@ +{ + "@context": "https://www.w3.org/ns/activitystreams", + "type": "OrderedCollection", + "totalItems": 0, + "orderedItems": [], +} diff --git a/users/test.inbox b/users/test.inbox new file mode 100644 index 0000000..a11f198 --- /dev/null +++ b/users/test.inbox @@ -0,0 +1,6 @@ +{ + "@context": "https://www.w3.org/ns/activitystreams", + "type": "OrderedCollection", + "totalItems": 0, + "orderedItems": [], +} diff --git a/users/test.jsonld b/users/test.jsonld index 3ad47cb..82cccc2 100644 --- a/users/test.jsonld +++ b/users/test.jsonld @@ -9,6 +9,8 @@ "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", diff --git a/users/test.outbox b/users/test.outbox new file mode 100644 index 0000000..bf8efb7 --- /dev/null +++ b/users/test.outbox @@ -0,0 +1 @@ +{"@context": "https://www.w3.org/ns/activitystreams", "type": "OrderedCollection", "totalItems": 22, "orderedItems": [{"@context": "https://www.w3.org/ns/activitystreams", "id": "https://0.exozy.me/users/test.outbox/create-hello-world", "type": "Create", "actor": "https://0.exozy.me/users/test.jsonld", "object": {"id": "https://0.exozy.me/users/test.outbox/hello-world", "type": "Note", "attributedTo": "https://0.exozy.me/users/test.jsonld", "inReplyTo": "https://social.exozy.me/@a/109707513227348721", "content": "Hello from fuwuqi!", "to": "https://www.w3.org/ns/activitystreams#Public"}}, {"@context": "https://www.w3.org/ns/activitystreams", "id": "https://0.exozy.me/users/test.outbox/hello-world", "type": "Create", "actor": "https://0.exozy.me/users/test.jsonld", "object": {"id": "https://0.exozy.me/users/test.statuses/hello-world", "type": "Note", "attributedTo": "https://0.exozy.me/users/test.jsonld", "inReplyTo": "https://social.exozy.me/@a/109707513227348721", "content": "Hello from fuwuqi!", "to": "https://www.w3.org/ns/activitystreams#Public"}}, {"@context": "https://www.w3.org/ns/activitystreams", "id": "https://0.exozy.me/users/test.outbox/hello-world", "type": "Create", "actor": "https://0.exozy.me/users/test.jsonld", "object": {"id": "https://0.exozy.me/users/test.statuses/hello-world", "type": "Note", "attributedTo": "https://0.exozy.me/users/test.jsonld", "inReplyTo": "https://social.exozy.me/@a/109707513227348721", "content": "Hello from fuwuqi!", "to": "https://www.w3.org/ns/activitystreams#Public"}}, {"@context": "https://www.w3.org/ns/activitystreams", "id": "https://0.exozy.me/users/test.outbox/hello-world", "type": "Create", "actor": "https://0.exozy.me/users/test.jsonld", "object": {"id": "https://0.exozy.me/users/test.statuses/hello-world", "type": "Note", "attributedTo": "https://0.exozy.me/users/test.jsonld", "inReplyTo": "https://social.exozy.me/@a/109707513227348721", "content": "Hello from fuwuqi!", "to": "https://www.w3.org/ns/activitystreams#Public"}}, {"@context": "https://www.w3.org/ns/activitystreams", "id": "https://0.exozy.me/users/test.outbox/hello-world", "type": "Create", "actor": "https://0.exozy.me/users/test.jsonld", "object": {"id": "https://0.exozy.me/users/test.statuses/hello-world", "type": "Note", "attributedTo": "https://0.exozy.me/users/test.jsonld", "inReplyTo": "https://social.exozy.me/@a/109707513227348721", "content": "Hello from fuwuqi!", "to": "https://www.w3.org/ns/activitystreams#Public"}}, {"@context": "https://www.w3.org/ns/activitystreams", "id": "https://0.exozy.me/users/test.outbox/hello-world", "type": "Create", "actor": "https://0.exozy.me/users/test.jsonld", "object": {"id": "https://0.exozy.me/users/test.statuses/hello-world", "type": "Note", "attributedTo": "https://0.exozy.me/users/test.jsonld", "inReplyTo": "https://social.exozy.me/@a/109707513227348721", "content": "Hello from fuwuqi!", "to": "https://www.w3.org/ns/activitystreams#Public"}}, {"@context": "https://www.w3.org/ns/activitystreams", "id": "https://0.exozy.me/users/test.outbox/hello-world", "type": "Create", "actor": "https://0.exozy.me/users/test.jsonld", "object": {"id": "https://0.exozy.me/users/test.statuses/hello-world", "type": "Note", "attributedTo": "https://0.exozy.me/users/test.jsonld", "inReplyTo": "https://social.exozy.me/@a/109707513227348721", "content": "Hello from fuwuqi!", "to": "https://www.w3.org/ns/activitystreams#Public"}}, {"@context": "https://www.w3.org/ns/activitystreams", "id": "https://0.exozy.me/users/test.outbox/hello-world", "type": "Create", "actor": "https://0.exozy.me/users/test.jsonld", "object": {"id": "https://0.exozy.me/users/test.statuses/hello-world", "type": "Note", "attributedTo": "https://0.exozy.me/users/test.jsonld", "inReplyTo": "https://social.exozy.me/@a/109707513227348721", "content": "Hello from fuwuqi!", "to": "https://www.w3.org/ns/activitystreams#Public"}}, {"@context": "https://www.w3.org/ns/activitystreams", "id": "https://0.exozy.me/users/test.outbox/hello-world", "type": "Create", "actor": "https://0.exozy.me/users/test.jsonld", "object": {"id": "https://0.exozy.me/users/test.statuses/hello-world", "type": "Note", "attributedTo": "https://0.exozy.me/users/test.jsonld", "inReplyTo": "https://social.exozy.me/@a/109707513227348721", "content": "Hello from fuwuqi!", "to": "https://www.w3.org/ns/activitystreams#Public"}}, {"@context": "https://www.w3.org/ns/activitystreams", "id": "https://0.exozy.me/users/test.outbox/hello-world", "type": "Create", "actor": "https://0.exozy.me/users/test.jsonld", "object": {"id": "https://0.exozy.me/users/test.statuses/hello-world", "type": "Note", "attributedTo": "https://0.exozy.me/users/test.jsonld", "inReplyTo": "https://social.exozy.me/@a/109707513227348721", "content": "Hello from fuwuqi!", "to": "https://www.w3.org/ns/activitystreams#Public"}}, {"@context": "https://www.w3.org/ns/activitystreams", "id": "https://0.exozy.me/users/test.outbox/hello-world", "type": "Create", "actor": "https://0.exozy.me/users/test.jsonld", "object": {"id": "https://0.exozy.me/users/test.statuses/hello-world", "type": "Note", "attributedTo": "https://0.exozy.me/users/test.jsonld", "inReplyTo": "https://social.exozy.me/@a/109707513227348721", "content": "Hello from fuwuqi!", "to": "https://www.w3.org/ns/activitystreams#Public"}}, {"@context": "https://www.w3.org/ns/activitystreams", "id": "https://0.exozy.me/users/test.outbox/hello-world", "type": "Create", "actor": "https://0.exozy.me/users/test.jsonld", "object": {"id": "https://0.exozy.me/users/test.statuses/hello-world", "type": "Note", "attributedTo": "https://0.exozy.me/users/test.jsonld", "inReplyTo": "https://social.exozy.me/@a/109707513227348721", "content": "Hello from fuwuqi!", "to": "https://www.w3.org/ns/activitystreams#Public"}}, {"@context": "https://www.w3.org/ns/activitystreams", "id": "https://0.exozy.me/users/test.outbox/hello-world", "type": "Create", "actor": "https://0.exozy.me/users/test.jsonld", "object": {"id": "https://0.exozy.me/users/test.statuses/hello-world", "type": "Note", "attributedTo": "https://0.exozy.me/users/test.jsonld", "inReplyTo": "https://social.exozy.me/@a/109707513227348721", "content": "Hello from fuwuqi!", "to": "https://www.w3.org/ns/activitystreams#Public"}}, {"@context": "https://www.w3.org/ns/activitystreams", "id": "https://0.exozy.me/users/test.outbox/hello-world", "type": "Create", "actor": "https://0.exozy.me/users/test.jsonld", "object": {"id": "https://0.exozy.me/users/test.statuses/hello-world", "type": "Note", "attributedTo": "https://0.exozy.me/users/test.jsonld", "inReplyTo": "https://social.exozy.me/@a/109707513227348721", "content": "Hello from fuwuqi!", "to": "https://www.w3.org/ns/activitystreams#Public"}}, {"@context": "https://www.w3.org/ns/activitystreams", "id": "https://0.exozy.me/users/test.outbox/hello-world", "type": "Create", "actor": "https://0.exozy.me/users/test.jsonld", "object": {"id": "https://0.exozy.me/users/test.statuses/hello-world", "type": "Note", "attributedTo": "https://0.exozy.me/users/test.jsonld", "inReplyTo": "https://social.exozy.me/@a/109707513227348721", "content": "Hello from fuwuqi!", "to": "https://www.w3.org/ns/activitystreams#Public"}}, {"@context": "https://www.w3.org/ns/activitystreams", "id": "https://0.exozy.me/users/test.outbox/hello-world", "type": "Create", "actor": "https://0.exozy.me/users/test.jsonld", "object": {"id": "https://0.exozy.me/users/test.statuses/hello-world", "type": "Note", "attributedTo": "https://0.exozy.me/users/test.jsonld", "inReplyTo": "https://social.exozy.me/@a/109707513227348721", "content": "Hello from fuwuqi!", "to": "https://www.w3.org/ns/activitystreams#Public"}}, {"@context": "https://www.w3.org/ns/activitystreams", "id": "https://0.exozy.me/users/test.outbox/hello-world", "type": "Create", "actor": "https://0.exozy.me/users/test.jsonld", "object": {"id": "https://0.exozy.me/users/test.statuses/hello-world", "type": "Note", "attributedTo": "https://0.exozy.me/users/test.jsonld", "inReplyTo": "https://social.exozy.me/@a/109707513227348721", "content": "Hello from fuwuqi!", "to": "https://www.w3.org/ns/activitystreams#Public"}}, {"@context": "https://www.w3.org/ns/activitystreams", "id": "https://0.exozy.me/users/test.outbox/hello-world", "type": "Create", "actor": "https://0.exozy.me/users/test.jsonld", "object": {"id": "https://0.exozy.me/users/test.statuses/hello-world", "type": "Note", "attributedTo": "https://0.exozy.me/users/test.jsonld", "inReplyTo": "https://social.exozy.me/@a/109707513227348721", "content": "Hello from fuwuqi!", "to": "https://www.w3.org/ns/activitystreams#Public"}}, {"@context": "https://www.w3.org/ns/activitystreams", "id": "https://0.exozy.me/users/test.outbox/hello-world", "type": "Create", "actor": "https://0.exozy.me/users/test.jsonld", "object": {"id": "https://0.exozy.me/users/test.statuses/hello-world", "type": "Note", "attributedTo": "https://0.exozy.me/users/test.jsonld", "inReplyTo": "https://social.exozy.me/@a/109707513227348721", "content": "Hello from fuwuqi!", "to": "https://www.w3.org/ns/activitystreams#Public"}}, {"@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"}}, {"@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"}}, {"@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"}}]} \ No newline at end of file diff --git a/users/test.statuses/hello-world b/users/test.statuses/hello-world new file mode 100644 index 0000000..40905c9 --- /dev/null +++ b/users/test.statuses/hello-world @@ -0,0 +1 @@ +{"id": "https://0.exozy.me/users/test.statuses/hello-world", "type": "Note", "attributedTo": "https://0.exozy.me/users/test.jsonld", "inReplyTo": "https://social.exozy.me/@a/109707513227348721", "content": "Hello from fuwuqi!", "to": "https://www.w3.org/ns/activitystreams#Public"} \ No newline at end of file diff --git a/users/test.statuses/hello-world2 b/users/test.statuses/hello-world2 new file mode 100644 index 0000000..1c826d1 --- /dev/null +++ b/users/test.statuses/hello-world2 @@ -0,0 +1 @@ +{"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"} \ No newline at end of file -- cgit v1.2.3-70-g09d2