Skip to main content





Revealed: a #crypto #billionaire’s political base hosting β€˜anti-woke’ and #rightwing activists in Westminster | #Politics | The Guardian


Convicted man moves abroad and then tries to disrupt his native country from afar on #immigration (hypocrite much?) and #abortion.

theguardian.com/politics/2026/…

#UK


tend2wobble reshared this.








"Encyclopedia Britannica and Merriam-Webster have filed a lawsuit against OpenAI, alleging in its complaint that the AI giant has committed 'massive copyright infringement.'" techcrunch.com/2026/03/16/merr…







🌴 Seph πŸ’­ πŸ‘Ύ reshared this.


Ok, I always love a cover where I wind up going 'what do you mean that's a cover?' Today gives us one, I'm sure many of us here remember this synth pop song, right?

youtu.be/lVrELhxOFnM

Would you believe that its a cover of a 20 year old pop-soul song written by Burt Bacharach and Hal David? Yeah, really, one of those how did this happen?

#Synth-Pop #Music #March-of-the-Covers #YouTube


🌴 Seph πŸ’­ πŸ‘Ύ reshared this.


Ok, and for tonight we have an unexpected cover, Stone Temple Pilots doing an acoustic rock/grunge cover of Led Zepplin's Dancing Days. Not as good as the original, but its a pretty cool cover nonetheless

youtu.be/Gu0kCdAgKJw

#YouTube #March-of-the-Covers #Music


🌴 Seph πŸ’­ πŸ‘Ύ reshared this.


Just a little music apropos of nothing

youtu.be/7Sw9Fh6uk4Q

#YouTube #Music


🌴 Seph πŸ’­ πŸ‘Ύ reshared this.


Hey y’all.

I’m looking for work as a senior software engineer or junior hardware dev. If you got any leads, DM me. I know Rust, C, C++, and Python.

reshared this





🌴 Seph πŸ’­ πŸ‘Ύ reshared this.


Happy Primary Day, Illinois!

Polls are open until 7 p.m. tonight.

Make sure you have a voting plan, and ask your friends and neighbors what their plan is for voting.

Every vote counts, folks. Let's go.

[contains quote post or other embedded content]

bsky.app/profile/citizensimpea…





I hate hold music, on the phone with the second investment firm, with a good hour so far between the two of them. Of the two, T Rowe Price has the worst hold music.

Vik was so disorganized, so far since he passed in July, I've had to deal with Fidelity, Morgan Stanley (I hate them with a passion), Merril Lynch (not impressed by them, despite knowing there was an account there, likely inherited from his mom, they can't find any record of either of them having an account there ever), and T Rowe Price. Definitely glad to be consolidating this down to a single financial planner who uses Charles Schwab. Not sure how good CS is, but if I had to choose one of the ones I've dealt with, it'd be Fidelity hands down, they've been incredibly easy to deal with.

#Life #Investments #Estate



🌴 Seph πŸ’­ πŸ‘Ύ reshared this.


Living in Missouri is so weird, its a Republican state that's made it clear that they don't care what voters want, just see the last few fights over ballot initiatives, and then you get things like this…

stlpr.org/government-politics-…

Considering minorities have a greater rate of incarceration, and minorities typically vote Democrat, I can't see an upside for them here, and yet here they are pushing this.

#USPol #MO #Voting #Missouri

This entry was edited (2 days ago)


🌴 Seph πŸ’­ πŸ‘Ύ reshared this.


huffpost.com/entry/wada-trump-…

#USPol



Wow, what a trainwreck#AI #law #lawfailure #ca #california

reshared this


🌴 Seph πŸ’­ πŸ‘Ύ reshared this.


Reminder, if you want a tumblr alternative, check wafrn: join.wafrn.net

You can migrate your existing bluesky account too!

This entry was edited (2 days ago)

reshared this








β™² @psych@diaspora.glasswings.com:
Contrast & Compare Department Memo # 47

No wonder the orange toddler is unhinged once again.


"The Obamas are competent, classy, loving, Harvard & Princeton graduates, loved by many, not on the #Epstein files, got the #Nobel Peace Prize, & gave us a steady economy to boot."

Aside from today's #unhinged #WordSalad rants (and yesterday's and day before's) - even while hiding out in Mar-a-Lago....
The old #BrainSpurs seem to be acting up too. Poor Donny #TrumpVirus has zero #ExecutiveFunctioning ability - meaning focus, comprehension, sequencing/planning/reasoning skills - and his repertoire of canned bullying and both-sidesism sound bites, is stale.

#TrumpVirus' florid irrationality is so grotesque & obvious that only his inner circle - the ones wearing oversized clown shoes - think or pretend there's nothing wrong or abnormal. Nothing to see here. Presidents always babble incoherently, start wars without goals or Congress, do 'deals' for the family while starving our children and gutting our society. Right, Donald. Even the #cult is tiring of this.








So, about today…
Lets see, its -6Β°C at the moment, but feels like -14 and they just revised the temperatures down to a high of -3. Thank goodness I just need to be at the chiropractor in an hour, then pick up a cup of coffee (St Louis Bread ain't great coffee, but its free coffee), and come home, no more out for today. Today is my first four week chiro visit, they've been three week visits since I hurt m'self, and the next one will be in four, but after that we might go six and see how things go, these visits are basically a permanent thing.
Lets see how many spoons I have left after taking care of more estate matters, gotta call two investment banks and depending on how things go, might have to tell the lawyer to unfile the probate matter and refile as a large estate, which is a double edged sword, more money for me, but instead of 2-3 months, this can take a year or more to resolve. Should do some cleaning, but… Ehhh, we'll see.
Yesterday wasn't much, grocery shopping and household finances done. Oh, slightly broke the DBA registry for Missouri by trying to register β˜…Generalβ˜… Delivery, while the stars didn't throw an error, they did get replaced by question marks as the system has no idea how to handle that.
Tomorrow will be work of course, thankfully it should be around 5 to 10 degrees warmer than today, but not sure about the wind, I think that'll less so the feels like'll be sane. Unfortunately work won't, Friday they were already making noises about it being so busy they need all drivers. Oh well, long day for me, and I'll be able to do an early start on Wed as they'll likely have routes that never went out.

#Weather #Medical #Today #Life #STL #St-Louis





@gabboman the wafrn dev Next version of Wafrn?


ActivityPub Server in a Single PHP File

shkspr.mobi/blog/2024/02/activ…
Any computer program can be designed to run from a single file if you architect it wrong enough!

I wanted to create the simplest possible Fediverse server which can be used as an educational tool to show how ActivityPub / Mastodon works.

The design goals were:

  • Upload a single PHP file to the server.
  • No databases or separate config files.
  • Single Actor (i.e. not multi-user).
  • Allow the Actor to be followed.
  • Post plain-text messages to followers.
  • Be roughly standards compliant.

And those goals have all been met! Check it out on GitLab. I warn you though, it is the nadir of bad coding. There are no tests, bugger-all security, scalability isn't considered, and it is a mess. But it works.

You can follow the test user @[url=https://example.viii.fi/example]example@example.viii.fi[/url]

Architecture


Firstly, I've slightly cheated on my "single file" stipulation. There's an .htaccess file which turns example.com/whatever into example.com/index.php?path=whatever

The index.php file then takes that path and does stuff. It also contains all the configuration variables which is very bad practice.

Rather than using a database, it saves files to disk.

Again, this is not suitable for any real world use. This is an educational tool to help explain the basics of posting messages to the Fediverse. It requires absolutely no dependencies. You do not need to spin up a dockerised hypervisor to manage your node bundles and re-compile everything to WASM. Just FTP the file up to prod and you're done.

Walkthrough


This is a quick ramble through the code. It is reasonably well documented, I hope.

Preamble


This is where you set up your account's name and bio. You also need to provide a public/private keypair. The posting page is protected with a password that also needs to be set here.
PHP // Set up the Actor's information
$username = rawurlencode("example"); // Encoded as it is often used as part of a URl
$realName = "E. Xample. Jr.";
$summary = "Some text about the user.";
$server = $_SERVER["SERVER_NAME"]; // Domain name this is hosted on

// Generate locally or from cryptotools.net/rsagen
// Newlines must be replaced with "\n"
$key_private = "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----";
$key_public = "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----";

// Password for sending messages
$password = "P4ssW0rd";

Logging


ActivityPub is a "chatty" protocol. This takes all the requests your server receives and saves them in /logs/ as a datestamped text file.
PHP // Get all headers and requests sent to this server
$headers = print_r( getallheaders(), true );
$postData = print_r( $_POST, true );
$getData = print_r( $_GET, true );
$filesData = print_r( $_FILES, true );
$body = json_decode( file_get_contents( "php://input" ), true );
$bodyData = print_r( $body, true );
$requestData = print_r( $_REQUEST, true );
$serverData = print_r( $_SERVER, true );

// Get the type of request - used in the log filename
if ( isset( $body["type"] ) ) {
$type = " " . $body["type"];
} else {
$type = "";
}

// Create a timestamp in ISO 8601 format for the filename
$timestamp = date( "c" );
// Filename for the log
$filename = "{$timestamp}{$type}.txt";

// Save headers and request data to the timestamped file in the logs directory
if( ! is_dir( "logs" ) ) { mkdir( "logs"); }

file_put_contents( "logs/{$filename}",
"Headers: \n$headers \n\n" .
"Body Data: \n$bodyData \n\n" .
"POST Data: \n$postData \n\n" .
"GET Data: \n$getData \n\n" .
"Files Data: \n$filesData \n\n" .
"Request Data:\n$requestData\n\n" .
"Server Data: \n$serverData \n\n"
);

Routing


The .htaccess changes /whatever to /?path=whateverThis runs the function of the path requested.
PHP !empty( $_GET["path"] ) ? $path = $_GET["path"] : die();
switch ($path) {
case ".well-known/webfinger":
webfinger();
case rawurldecode( $username ):
username();
case "following":
following();
case "followers":
followers();
case "inbox":
inbox();
case "write":
write();
case "send":
send();
default:
die();
}

WebFinger


The WebFinger Protocol is used to identify accounts. It is requested with example.com/.well-known/webfinger?resource=acct:username@example.comThis server only has one user, so it ignores the query string and always returns the same details.
PHP function webfinger() {
global $username, $server;

$webfinger = array(
"subject" => "acct:{$username}@{$server}",
"links" => array(
array(
"rel" => "self",
"type" => "application/activity+json",
"href" => "https://{$server}/{$username}"
)
)
);
header( "Content-Type: application/json" );
echo json_encode( $webfinger );
die();
}

Username


Requesting example.com/username returns a JSON document with the user's information.
PHP function username() {
global $username, $realName, $summary, $server, $key_public;

$user = array(
"@context" => [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1"
],
"id" => "https://{$server}/{$username}",
"type" => "Person",
"following" => "https://{$server}/following",
"followers" => "https://{$server}/followers",
"inbox" => "https://{$server}/inbox",
"preferredUsername" => rawurldecode($username),
"name" => "{$realName}",
"summary" => "{$summary}",
"url" => "https://{$server}",
"manuallyApprovesFollowers" => true,
"discoverable" => true,
"published" => "2024-02-12T11:51:00Z",
"icon" => [
"type" => "Image",
"mediaType" => "image/png",
"url" => "https://{$server}/icon.png"
],
"publicKey" => [
"id" => "https://{$server}/{$username}#main-key",
"owner" => "https://{$server}/{$username}",
"publicKeyPem" => $key_public
]
);
header( "Content-Type: application/activity+json" );
echo json_encode( $user );
die();
}

Following & Followers


These JSON documents show how many users are following / followers-of this account. The information here is self-attested. So you can lie and use any number you want.
PHPfunction following() {
global $server;

$following = array(
"@context" => "https://www.w3.org/ns/activitystreams",
"id" => "https://{$server}/following",
"type" => "Collection",
"totalItems" => 0,
"items" =>
[] );
header( "Content-Type: application/activity+json" );
echo json_encode( $following );
die();
}
function followers() {
global $server;
$followers = array(
"@context" => "https://www.w3.org/ns/activitystreams",
"id" => "https://{$server}/followers",
"type" => "Collection",
"totalItems" => 0,
"items" =>
[] );
header( "Content-Type: application/activity+json" );
echo json_encode( $followers );
die();
}

Inbox


The /inbox is the main server. It receives all requests. This server only responds to "Follow" requests. A remote server sends a follow request which is a JSON file saying who they are. This code does not cryptographically validate the headers of the received message. The name of the remote user's server is saved to a file so that future messages can be delivered to it. An accept request is cryptographically signed and POST'd back to the remote server.
PHP function inbox() {
global $body, $server, $username, $key_private;

// Get the message and type
$inbox_message = $body;
$inbox_type = $inbox_message["type"];

// This inbox only responds to follow requests
if ( "Follow" != $inbox_type ) { die(); }

// Get the parameters
$inbox_id = $inbox_message["id"];
$inbox_actor = $inbox_message["actor"];
$inbox_host = parse_url( $inbox_actor, PHP_URL_HOST );

// Does this account have any followers?
if( file_exists( "followers.json" ) ) {
$followers_file = file_get_contents( "followers.json" );
$followers_json = json_decode( $followers_file, true );
} else {
$followers_json = array();
}

// Add user to list. Don't care about duplicate users, server is what's important
$followers_json[$inbox_host]["users"][] = $inbox_actor;

// Save the new followers file
file_put_contents( "followers.json", print_r( json_encode( $followers_json ), true ) );

// Response Message ID
// This isn't used for anything important so could just be a random number
$guid = uuid();

// Create the Accept message
$message = [
"@context" => "https://www.w3.org/ns/activitystreams",
"id" => "https://{$server}/{$guid}",
"type" => "Accept",
"actor" => "https://{$server}/{$username}",
"object" => [
"@context" => "https://www.w3.org/ns/activitystreams",
"id" => $inbox_id,
"type" => $inbox_type,
"actor" => $inbox_actor,
"object" => "https://{$server}/{$username}",
]
];

// The Accept is sent to the server of the user who requested the follow
// TODO: The path doesn't *always* end with/inbox
$host = $inbox_host;
$path = parse_url( $inbox_actor, PHP_URL_PATH ) . "/inbox";

// Get the signed headers
$headers = generate_signed_headers( $message, $host, $path );

// Specify the URL of the remote server's inbox
// TODO: The path doesn't *always* end with /inbox
$remoteServerUrl = $inbox_actor . "/inbox";

// POST the message and header to the requester's inbox
$ch = curl_init( $remoteServerUrl );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, "POST" );
curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode($message) );
curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
$response = curl_exec( $ch );

// Check for errors
if( curl_errno( $ch ) ) {
file_put_contents( "error.txt", curl_error( $ch ) );
}
curl_close($ch);
die();
}

UUID


Every message sent should have a unique ID. This can be anything you like. Some servers use a random number. I prefer a date-sortable string.
PHP function uuid() {
return sprintf( "%08x-%04x-%04x-%04x-%012x",
time(),
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0x3fff) | 0x8000,
mt_rand(0, 0xffffffffffff)
);
}

Signing Headers


Every message that your server sends needs to be cryptographically signed with your Private Key. This is a complicated process. Please read "How to make friends and verify requests" for more information.
PHP function generate_signed_headers( $message, $host, $path ) {
global $server, $username, $key_private;

// Encode the message to JSON
$message_json = json_encode( $message );

// Location of the Public Key
$keyId = "https://{$server}/{$username}#main-key";

// Generate signing variables
$hash = hash( "sha256", $message_json, true );
$digest = base64_encode( $hash );
$date = date( "D, d M Y H:i:s \G\M\T" );

// Get the Private Key
$signer = openssl_get_privatekey( $key_private );

// Sign the path, host, date, and digest
$stringToSign = "(request-target): post $path\nhost: $host\ndate: $date\ndigest: SHA-256=$digest";

// The signing function returns the variable $signature
// php.net/manual/en/function.ope…
openssl_sign(
$stringToSign,
$signature,
$signer,
OPENSSL_ALGO_SHA256
);
// Encode the signature
$signature_b64 = base64_encode( $signature );

// Full signature header
$signature_header = 'keyId="' . $keyId . '",algorithm="rsa-sha256",headers="(request-target) host date digest",signature="' . $signature_b64 . '"';

// Header for POST reply
$headers = array(
"Host: {$host}",
"Date: {$date}",
"Digest: SHA-256={$digest}",
"Signature: {$signature_header}",
"Content-Type: application/activity+json",
"Accept: application/activity+json",
);

return $headers;
}

User Interface for Writing


This creates a basic HTML form. Type in your message and your password. It then POSTs the data to the /send endpoint.
PHP function write() {
// Display an HTML form for the user to enter a message.echo <<< HTML
<!DOCTYPE html>
<html lang="en-GB">
<head>
<meta charset="UTF-8">
<title>Send Message</title>
<style>
*{font-family:sans-serif;font-size:1.1em;}
</style>
</head>
<body>
<form action="/send" method="post" enctype="multipart/form-data">
<label for="content">Your message:</label><br>
<textarea id="content" name="content" rows="5" cols="32"></textarea><br>
<label for="password">Password</label><br>
<input type="password" name="password" id="password" size="32"><br>
<input type="submit" value="Post Message">
</form>
</body>
</html>
HTML;
die();
}

Send Endpoint


This takes the submitted message and checks the password is correct. It reads the followers.json file and sends the message to every server that is following this account.
PHP function send() {
global $password, $server, $username, $key_private;

// Does the posted password match the stored password?
if( $password != $_POST["password"] ) { die(); }

// Get the posted content
$content = $_POST["content"];

// Current time - ISO8601
$timestamp = date( "c" );

// Outgoing Message ID
$guid = uuid();

// Construct the Note
// contentMap is used to prevent unnecessary "translate this post" pop ups
// hardcoded to English
$note = [
"@context" => array(
"https://www.w3.org/ns/activitystreams"
),
"id" => "https://{$server}/posts/{$guid}.json",
"type" => "Note",
"published" => $timestamp,
"attributedTo" => "https://{$server}/{$username}",
"content" => $content,
"contentMap" => ["en" => $content],
"to" => ["https://www.w3.org/ns/activitystreams#Public"]
];

// Construct the Message
$message = [
"@context" => "https://www.w3.org/ns/activitystreams",
"id" => "https://{$server}/posts/{$guid}.json",
"type" => "Create",
"actor" => "https://{$server}/{$username}",
"to" => [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc" => [
"https://{$server}/followers"
],
"object" => $note
];

// Create the context for the permalink
$note = [ "@context" => "https://www.w3.org/ns/activitystreams", ...$note ];

// Save the permalink
$note_json = json_encode( $note );
// Check for posts/ directory and create it
if( ! is_dir( "posts" ) ) { mkdir( "posts"); }
file_put_contents( "posts/{$guid}.json", print_r( $note_json, true ) );

// Read existing users and get their hosts
$followers_file = file_get_contents( "followers.json" );
$followers_json = json_decode( $followers_file, true );
$hosts = array_keys( $followers_json );

// Prepare to use the multiple cURL handle
$mh = curl_multi_init();

// Loop through all the severs of the followers
// Each server needs its own cURL handle
// Each POST to an inbox needs to be signed separately
foreach ( $hosts as $host ) {
$path = "/inbox";

// Get the signed headers
$headers = generate_signed_headers( $message, $host, $path );

// Specify the URL of the remote server
$remoteServerUrl = "https://{$host}{$path}";

// POST the message and header to the requester's inbox
$ch = curl_init( $remoteServerUrl );

curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, "POST" );
curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode($message) );
curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );

// Add the handle to the multi-handle
curl_multi_add_handle( $mh, $ch );
}

// Execute the multi-handle
do {
$status = curl_multi_exec( $mh, $active );
if ( $active ) {
curl_multi_select( $mh );
}
} while ( $active && $status == CURLM_OK );

// Close the multi-handle
curl_multi_close( $mh );

// Render the JSON so the user can see the POST has worked
header( "Location: https://{$server}/posts/{$guid}.json" );
die();
}

Next Steps


This is not intended to be used in production. Ever. But if you would like to contribute more simple examples of how the protocol works, please come and play on GitLab.

You can follow the test user @[url=https://example.viii.fi/example]example@example.viii.fi[/url]
#ActivityPub #mastodon #php


gabboman the wafrn dev reshared this.


🌴 Seph πŸ’­ πŸ‘Ύ reshared this.


So today's song is an interesting one, this is the version I know from the a NASCAR CD, Runnin' Wide Open, basically a compilation album full of car related songs. What I didn't know and surprised me when SXM played the original is that this is a Bruce Springsteen song. I swear, Springsteen is like Prince, his name turns up in the most unexpected places as a songwriter and/or the original recorder of songs.

youtu.be/9NlLf9_6f_8

#Country #YouTube #March-of-the-Covers #Music







🌴 Seph πŸ’­ πŸ‘Ύ reshared this.


Hi! My name is Anna! I’m a traditional artist from Kyiv πŸ‡ΊπŸ‡¦ . I paint, I adore cats, and I love working out at the gym.

I’m always working on my English, but sometimes I might post in Ukrainian because I don’t always feel like double-checking every word. I hope that’s okay.

I’m not much of a talker, and I’m not quite sure yet what exactly I’ll be posting here, but I wanted to give it a try🫢

#Introduction #Знайомство

reshared this


🌴 Seph πŸ’­ πŸ‘Ύ reshared this.


@TheBreadmonkey

"Our pasta, who art in a colander, draining be your noodles. Thy noodle come, Thy sauce be yum, on top some grated Parmesan. Give us this day, our garlic bread, …and forgive us our trespasses, as we forgive those who trample on our lawns. And lead us not into vegetarianism, but deliver us some pizza, for thine is the meatball, the noodle, and the sauce, forever and ever. R'Amen."

May His noodly appendages touch you

@Ben

reshared this


🌴 Seph πŸ’­ πŸ‘Ύ reshared this.


Look here world!

If you send ships to bail out #Trump in the #StraitOfHormuz, he’ll reduce your tariffs by 1% & stop calling you parasites. Okay?

Would the #TrumpRegime lie?

Alright. He’ll also stop his plans to invade your country AND stop funding agitators in your country as well.

So where are your ships?!

#NeverTrustTrump #USPol #auspol #Iran #TrumpMadeThisMess

reshared this

⇧