Introduction
The Old Dog and Duck
A Clojure library designed to implement the ActivityPub protocol, obviously.

@@ -19,8 +19,24 @@
Delivering ActivityStreams objects to peers;
Delivering ActivityStreams objects to clients.
-
NOTE THAT what Mastodon delivers to clients is not actually in ActivityStreams format; this seems to be an ad-hoc hack that’s just never been fixed and has therefore become a de-facto standard for communication between ActivityPub hosts and their clients.
+
Some motivations
+
Empowering users
+
The ActivityPub spec starts by saying:
+
+ ActivityPub provides two layers:
+
+ - A server to server federation protocol (so decentralized websites can share information)
+ - A client to server protocol (so users, including real-world users, bots, and other automated processes, can communicate with ActivityPub using their accounts on servers, from a phone or desktop or web application or whatever).
+
+
+
I’m interested in driving much more functionality down to the the client, for example feed ordering and presentation. This would allow users, for example, to choose their own (or roll their own) feed-ordering algorithms.
My proposal would be to deliver exactly the same ActivityStreams format to my client as to other servers. There may be a valid reason for not doing this, but if there is I will discover it in due course.
+
Enhanced resiliency
+
The ActivityStreams spec seems predicated on ‘always up’ communication between at least servers, which is perhaps why there is a two tier network of ‘servers’ and ‘clients’. It also depends on HTTPS certificates to identify servers, which implies it’s vulnerable to disruption by a hostile actor with the ability to revoke certificates.
+
My own history with social media dates back to Usenet over UUCP, a system designed explicitly for intermittent low bandwidth connections; such a system is immensely resilient in the face of disruption to infrastructure.
+
Social media is useful to concerted popular action in periods of disruption, whether in the case of civil ememrgency such as earthquakes, wild fires and floods, in the case of wars, or in the case of intrusive surveillance by authoritarian governments. But to be useful in such situations it needs to be resilient, and one of the things it needs to be resilient to is parts of the network being intermittently available, or requiring rerouting.
+
In this I’m influenced by and hope to try to implement ideas from Ian Clarke’s Freenet and Tahrir projects, especially webs of trust.
+
To be clear, it is important for The Old Dog and Duck to be able to interact with other existing ‘vanilla’ ActivityStreams implementations, but I hope to experiment with enhanced communication between Dog and Duck servers to provide more FreeNet-like resiliency.
Proposed dog-and-duck libraries
NOTE THAT at the present stage all the proposed libraries are in one package, namely this package, but that it is proposed that in future they will form separate libraries in separate packages.
Bar
diff --git a/project.clj b/project.clj
index b0ba436..523d98e 100644
--- a/project.clj
+++ b/project.clj
@@ -7,15 +7,14 @@
:output-path "docs/codox"
:source-uri "https://github.com/simon-brooke/dog-and-duck/blob/master/{filepath}#L{line}"}
:description "A playground for hacking ActivityPub stuff."
- :dependencies [[org.clojure/clojure "1.11.1"]
+ :dependencies [[clj-activitypub/activitypub "0.49"]
+ [com.taoensso/timbre "6.0.4"]
+ [mvxcvi/clj-pgp "1.1.0"]
+ [org.bouncycastle/bcpkix-jdk18on "1.72"]
+ [org.clojure/clojure "1.11.1"]
[org.clojure/data.json "2.4.0"]
[org.clojure/math.numeric-tower "0.0.5"]
- [org.clojure/spec.alpha "0.3.218"]
- [mvxcvi/clj-pgp "1.1.0"]
- [org.bouncycastle/bcpkix-jdk18on "1.72"] ;; required by clj-activitypub
- [clj-http "3.12.3"] ;; required by clj-activitypub
- [cheshire "5.11.0"] ;; if this is not present, clj-http/client errors with 'json-enabled?'
- [com.taoensso/timbre "6.0.4"]]
+ [org.clojure/spec.alpha "0.3.218"]]
:license {:name "GPL-2.0-or-later"
:url "https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html"}
:plugins [[lein-cloverage "1.2.2"]
diff --git a/src/dog_and_duck/activitypub.code-workspace b/src/dog_and_duck/activitypub.code-workspace
new file mode 100644
index 0000000..b1e3e48
--- /dev/null
+++ b/src/dog_and_duck/activitypub.code-workspace
@@ -0,0 +1,11 @@
+{
+ "folders": [
+ {
+ "path": "../.."
+ },
+ {
+ "path": "../../../clj-activitypub"
+ }
+ ],
+ "settings": {}
+}
\ No newline at end of file
diff --git a/src/dog_and_duck/quack/picky.clj b/src/dog_and_duck/quack/picky.clj
index 54dbed2..113eb57 100644
--- a/src/dog_and_duck/quack/picky.clj
+++ b/src/dog_and_duck/quack/picky.clj
@@ -302,11 +302,12 @@
(defn activity-faults
[x]
- (concat-non-empty (persistent-object-faults x)
- (activity-type-faults x)
- (list
- (when-not
- (has-activity-type? x)
- (make-fault-object :must :not-activity-type))
- (when-not (string? (:summary x)) (make-fault-object :should :no-summary)))))
+ (concat-non-empty
+ (persistent-object-faults x)
+ (activity-type-faults x)
+ (list
+ (when-not
+ (has-activity-type? x)
+ (make-fault-object :must :not-activity-type))
+ (when-not (string? (:summary x)) (make-fault-object :should :no-summary)))))
diff --git a/src/dog_and_duck/quack/picky/required_properties.clj b/src/dog_and_duck/quack/picky/required_properties.clj
index e69de29..aeedcde 100644
--- a/src/dog_and_duck/quack/picky/required_properties.clj
+++ b/src/dog_and_duck/quack/picky/required_properties.clj
@@ -0,0 +1,2 @@
+(ns dog-and-duck.quack.picky.required-properties)
+
diff --git a/src/dog_and_duck/scratch/scratch.clj b/src/dog_and_duck/scratch/scratch.clj
index 119533f..f1795ed 100644
--- a/src/dog_and_duck/scratch/scratch.clj
+++ b/src/dog_and_duck/scratch/scratch.clj
@@ -2,8 +2,11 @@
"Scratchpad where I try to understand how to do this stuff."
(:require [clj-activitypub.core :as activitypub]
[clj-activitypub.webfinger :as webfinger]
+ [clj-activitypub.net :as activitypub-net]
[clj-pgp.generate :as pgp-gen]
- [clojure.walk :refer [keywordize-keys]]))
+ [clojure.walk :refer [keywordize-keys]]
+ [clojure.pprint :refer [pprint]]
+ [clojure.data.json :as json]))
;;; Copyright (C) Simon Brooke, 2022
@@ -24,23 +27,29 @@
;;; Use any ActivityPub account handle you like - for example, your own
(def account-handle "@simon_brooke@mastodon.scot")
-(def handle (activitypub/parse-account account-handle))
-(webfinger/fetch-user-id "mastodon.scot" "simon_brooke")
-(apply webfinger/fetch-user-id (map handle [:domain :username]))
+;;(def handle (activitypub/parse-account account-handle))
+;;(webfinger/fetch-user-id "mastodon.scot" "simon_brooke")
+;;(apply webfinger/fetch-user-id (map handle [:domain :username]))
;;; Retrieve the account details from its home server
;;; (`keywordize-keys` is not necessary here but produces a more idiomatic clojure
;;; data structure)
(def account
- "Fetch my account to mess with"
- (let [handle (activitypub/parse-account account-handle)]
- (keywordize-keys
- (activitypub/fetch-user
- (apply webfinger/fetch-user-id (map handle [:domain :username]))))))
+ (-> account-handle
+ (webfinger/parse-handle)
+ (webfinger/fetch-user-id!)
+ (activitypub-net/fetch-user!)
+ (select-keys [:name :preferredUsername :inbox :summary])))
-;;; examine what you got back!
+;; ;;; examine what you got back!
(:inbox account)
+(-> account
+ :inbox
+ slurp
+ json/read-str
+ pprint) ;; => 80
+
;; (def rsa (pgp-gen/rsa-keypair-generator 2048))
;; (def kp (pgp-gen/generate-keypair rsa :rsa-general))