Smithereen joins the pasture (tools/pasture/peers/smithereen.sh): its image on the shared MySQL, with imgproxy and a file server behind Caddy, a JDK trust store with the pasture's CA, accounts from its signup form, and a password grant for a local application. scenarios/smithereen.sh drives its VKontakte-like API: friends as mutual follows, wall posts, comments, likes, reposts (quotes there), polls, edits, deletions, private messages, the unfollow and statistics, 30 checks. A post on someone else's wall never reaches PrivaPub, since Smithereen sends it only to servers whose actors publish a wall: G-0009, waiting for the owner. PrivaPub: a server description that failed (Smithereen serves no NodeInfo until it has a description) frees its week, so the server's next arrival asks again instead of a week later. The shared Postgres takes 400 connections: at 100 the village seed ran it dry (Sharkey's API answered 500, Misskey dropped deliveries). The seeder records a follow that stands from an earlier seed when the step itself fails, so the checker no longer expects that follower to see nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
126 lines
7.7 KiB
Bash
126 lines
7.7 KiB
Bash
# Smithereen 1.0.3: a VKontakte-like network in Java. Walls (posts on someone else's wall carry a `target`, and the wall's
|
|
# owner announces them with Add{Note}), friends as mutual follows (requests as Offer{Follow}), groups and events, photo
|
|
# albums, threaded comments, polls, private messages. On the shared MySQL (database smithereen), with imgproxy for its
|
|
# pictures and a file server for its uploads, both behind Caddy as smithereen.test (/i and /s). Its API is VKontakte's
|
|
# (/api/method/<name>?v=1.0) and takes a password grant for an Application its OAuth knows: the pasture makes a local
|
|
# one in its database. Its HTTP client refuses private addresses, which the pasture's subnet is not.
|
|
SMITHEREEN_IMAGE=${SMITHEREEN_IMAGE:-docker.io/grishkaa/smithereen@sha256:fcef096b78de21cc98936c2cebca5b5217d10976e59886be9e68d78f650581cf}
|
|
# the commit that image was built from (its version.properties), whose schema.sql makes the database
|
|
SMITHEREEN_COMMIT=d60c6e4c5abf2682d203f948770e6f63ce3bdeb5
|
|
SMITHEREEN_IMGPROXY_IMAGE=${SMITHEREEN_IMGPROXY_IMAGE:-docker.io/darthsim/imgproxy:v3.31.4}
|
|
SMITHEREEN_PASSWORD=Smithereen-Pasture-Pass-1
|
|
# imgproxy's URL signing pair, shared with Smithereen's config: not a secret in the pasture
|
|
SMITHEREEN_IMGPROXY_KEY=736d697468657265656e2d706173747572652d696d6770726f78792d6b65792d
|
|
SMITHEREEN_IMGPROXY_SALT=736d697468657265656e2d706173747572652d696d6770726f78792d73616c74
|
|
. "$here/peers/shared.sh"
|
|
|
|
smithereen_sql() { podman exec -i pasture-mysql mysql -uroot -ppasture -N smithereen "$@" 2>/dev/null; }
|
|
|
|
smithereen_up() {
|
|
shared_mysql_up
|
|
# its schema updater makes stored functions, which MySQL refuses to a user without SUPER while binary logging is on
|
|
podman exec pasture-mysql mysql -uroot -ppasture -e "set persist log_bin_trust_function_creators = 1" 2>/dev/null
|
|
local st="$here/.state/smithereen"
|
|
mkdir -p "$st"
|
|
[ -s "$st/schema.sql" ] || curl -fsSL "https://raw.githubusercontent.com/grishka/Smithereen/$SMITHEREEN_COMMIT/schema.sql" -o "$st/schema.sql" \
|
|
|| { echo "smithereen: no schema.sql" >&2; return 1; }
|
|
if [ "$(echo "select count(*) from information_schema.tables where table_schema='smithereen' and table_name='users'" | smithereen_sql)" != "1" ]; then
|
|
mysql_db smithereen
|
|
smithereen_sql < "$st/schema.sql"
|
|
# anyone may sign up, without a captcha or a confirmation mail: the pasture makes its accounts through the form. Its
|
|
# NodeInfo fails (a NullPointerException) until the server has a short description
|
|
echo "insert into config (\`key\`, value) values ('SignupMode', 'OPEN'), ('SignupFormUseCaptcha', '0'), ('SignupConfirmEmail', '0'),
|
|
('ServerDisplayName', 'Smithereen pasture'), ('ServerShortDescription', 'The pasture''s Smithereen')
|
|
on duplicate key update value=values(value)" | smithereen_sql
|
|
fi
|
|
cat > "$st/config.properties" <<-EOF
|
|
db.host=mysql
|
|
db.name=smithereen
|
|
db.user=pasture
|
|
db.password=pasture
|
|
server.ip=0.0.0.0
|
|
domain=smithereen.test
|
|
upload.path=/uploads/uploads
|
|
upload.url_path=/s/uploads
|
|
media_cache.path=/uploads/media_cache
|
|
media_cache.url_path=/s/media_cache
|
|
media_cache.max_size=1G
|
|
media_cache.file_size_limit=50M
|
|
imgproxy.url_prefix=/i
|
|
imgproxy.local_uploads=/uploads
|
|
imgproxy.local_media_cache=/media_cache
|
|
imgproxy.key=$SMITHEREEN_IMGPROXY_KEY
|
|
imgproxy.salt=$SMITHEREEN_IMGPROXY_SALT
|
|
EOF
|
|
# Java trusts its own store only: the JDK's, with the pasture's CA added
|
|
if [ ! -s "$st/cacerts" ]; then
|
|
podman run --rm --entrypoint sh -v "$st:/st:Z" -v "$ca:/pasture/ca:z,ro" $SMITHEREEN_IMAGE -c \
|
|
'cp /opt/java/openjdk/lib/security/cacerts /st/cacerts && chmod u+w /st/cacerts &&
|
|
/opt/java/openjdk/bin/keytool -importcert -noprompt -alias pasture -file /pasture/ca/root.crt -keystore /st/cacerts -storepass changeit' >/dev/null
|
|
fi
|
|
podman volume exists pasture-smithereen-uploads || podman volume create --label pasture=1 pasture-smithereen-uploads >/dev/null
|
|
podman run -d --replace --name pasture-smithereen-imgproxy --label pasture=1 --network $net -v pasture-smithereen-uploads:/uploads \
|
|
-e IMGPROXY_PATH_PREFIX=/i -e IMGPROXY_ALLOWED_SOURCES=local:// -e IMGPROXY_LOCAL_FILESYSTEM_ROOT=/uploads \
|
|
-e IMGPROXY_KEY=$SMITHEREEN_IMGPROXY_KEY -e IMGPROXY_SALT=$SMITHEREEN_IMGPROXY_SALT $SMITHEREEN_IMGPROXY_IMAGE >/dev/null
|
|
podman run -d --replace --name pasture-smithereen-files --label pasture=1 --network $net -v pasture-smithereen-uploads:/srv:ro \
|
|
docker.io/library/caddy:2 caddy file-server --root /srv --listen :80 >/dev/null
|
|
podman run -d --replace --name pasture-smithereen --label pasture=1 --network $net -v pasture-smithereen-uploads:/uploads \
|
|
-v "$st/config.properties:/usr/local/etc/config.properties:Z,ro" -v "$st/cacerts:/pasture/cacerts:Z,ro" \
|
|
-e JAVA_TOOL_OPTIONS='-Djavax.net.ssl.trustStore=/pasture/cacerts -Djavax.net.ssl.trustStorePassword=changeit -Xmx512m' \
|
|
$SMITHEREEN_IMAGE >/dev/null
|
|
podman exec pasture-smithereen mkdir -p /uploads/uploads /uploads/media_cache
|
|
smithereen_wait
|
|
smithereen_settle
|
|
echo "smithereen: https://smithereen.test:6443"
|
|
}
|
|
|
|
smithereen_wait() {
|
|
for _ in $(seq 1 90); do
|
|
site smithereen.test -s -o /dev/null -w '%{http_code}' https://smithereen.test:6443/.well-known/nodeinfo 2>/dev/null | grep -q 200 && return 0
|
|
sleep 2
|
|
done
|
|
echo "smithereen did not start" >&2; return 1
|
|
}
|
|
|
|
# smithereen_account <name> <first name>: an account made through the signup form, then renamed from its id<N> to the
|
|
# name (Smithereen leaves the name to its settings page)
|
|
smithereen_account() {
|
|
local name=$1 first=$2
|
|
[ "$(echo "select count(*) from users where username='$name' and domain=''" | smithereen_sql)" = "1" ] && return 0
|
|
site smithereen.test -s -o /dev/null -X POST https://smithereen.test:6443/account/register \
|
|
--data-urlencode "email=$name@smithereen.test" --data-urlencode "password=$SMITHEREEN_PASSWORD" \
|
|
--data-urlencode "password2=$SMITHEREEN_PASSWORD" --data-urlencode "first_name=$first" --data-urlencode "last_name=Pasture"
|
|
echo "update users set username='$name' where id=(select user_id from accounts where email='$name@smithereen.test')" | smithereen_sql
|
|
}
|
|
|
|
# smuser and smfriend, a local client application for the password grant, and smuser's token
|
|
smithereen_settle() {
|
|
local st="$here/.state/smithereen"
|
|
smithereen_account smuser Smitty
|
|
smithereen_account smfriend Freddie
|
|
if [ "$(echo "select count(*) from api_applications where name='pasture'" | smithereen_sql)" = "0" ]; then
|
|
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out "$st/app.pem" 2>/dev/null
|
|
local pub priv
|
|
pub=$(openssl pkey -in "$st/app.pem" -pubout -outform DER | python3 -c 'import sys; print(sys.stdin.buffer.read().hex())')
|
|
priv=$(openssl pkcs8 -topk8 -nocrypt -in "$st/app.pem" -outform DER | python3 -c 'import sys; print(sys.stdin.buffer.read().hex())')
|
|
echo "insert into api_applications (type, name, description, developer_id, public_key, private_key, extra)
|
|
values (0, 'pasture', 'the pasture', 1, x'$pub', x'$priv', '{\"redirectURIs\":[\"https://smithereen.test/\"]}')" | smithereen_sql
|
|
fi
|
|
# the new names are read at start
|
|
podman restart pasture-smithereen >/dev/null
|
|
smithereen_wait
|
|
local app
|
|
app=$(echo "select id from api_applications where name='pasture'" | smithereen_sql)
|
|
echo "https://smithereen.test/apps/$app" > "$st/client_id"
|
|
# (its login flood control may refuse a second grant at once: tried again)
|
|
for user in smuser smfriend; do
|
|
for _ in 1 2 3 4 5; do
|
|
site smithereen.test -s -X POST https://smithereen.test:6443/oauth/token --data-urlencode grant_type=password \
|
|
--data-urlencode "client_id=https://smithereen.test/apps/$app" --data-urlencode "username=$user@smithereen.test" \
|
|
--data-urlencode "password=$SMITHEREEN_PASSWORD" | python3 -c 'import json, sys; print(json.load(sys.stdin).get("access_token", ""))' > "$st/$user.token"
|
|
[ -s "$st/$user.token" ] && [ "$(wc -c < "$st/$user.token")" -gt 2 ] && break
|
|
sleep 3
|
|
done
|
|
done
|
|
}
|