WIP: consolidate, finally

This commit is contained in:
a 2024-10-12 13:45:14 -05:00
parent ba6b5c94d3
commit 29bc506741
359 changed files with 368 additions and 12924 deletions

View file

@ -1,6 +0,0 @@
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: true
---

View file

@ -1,15 +0,0 @@
/*
Use a window's inner dimensions for viewport units.
This fixes some mobile bugs
*/
var root = document.documentElement;
let vh = window.innerHeight * 0.01;
root.style.setProperty('--vh', `${vh}px`);
// We listen to the resize event
window.addEventListener('resize', () => {
// We execute the same script as before
let vh = window.innerHeight * 0.01;
root.style.setProperty('--vh', `${vh}px`);
});

View file

@ -1,226 +0,0 @@
/*
tutorials used:
- https://aaronluna.dev/blog/add-search-to-static-site-lunrjs-hugo-vanillajs/#codepen-with-final-code
- https://victoria.dev/blog/add-search-to-hugo-static-sites-with-lunr/
*/
let pagesIndex, searchIndex
const MAX_SUMMARY_LENGTH = 30
const SENTENCE_BOUNDARY_REGEX = /\b\.\s/gm
const WORD_REGEX = /\b(\w*)[\W|\s|\b]?/gm
async function initSearch() {
try {
const indexJsonUrl = document.getElementById("base-url").dataset["baseUrl"] + "/index.json";
const response = await fetch(indexJsonUrl);
pagesIndex = await response.json();
searchIndex = lunr(function () {
this.field("title");
this.field("content");
this.ref("href");
pagesIndex.forEach((page) => this.add(page));
});
} catch (e) {
console.log(e);
}
console.log("Search index initialized")
// Get the query parameter(s)
const params = new URLSearchParams(window.location.search)
const query = params.get('query')
// Perform a search if there is a query
if (query) {
// Retain the search input in the form when displaying results
document.getElementById('search-input').setAttribute('value', query)
// Update the list with results
console.log("search performed")
let results = searchSite(query)
renderSearchResults(query, results)
}
}
initSearch();
function searchSite(query) {
const originalQuery = query;
query = getLunrSearchQuery(query);
let results = getSearchResults(query);
return results.length
? results
: query !== originalQuery
? getSearchResults(originalQuery)
: [];
}
function getLunrSearchQuery(query) {
const searchTerms = query.split(" ");
if (searchTerms.length === 1) {
return query;
}
query = "";
for (const term of searchTerms) {
query += `+${term} `;
}
return query.trim();
}
function getSearchResults(query) {
return searchIndex.search(query).flatMap((hit) => {
if (hit.ref == "undefined") return [];
let pageMatch = pagesIndex.filter((page) => page.href === hit.ref)[0];
pageMatch.score = hit.score;
return [pageMatch];
});
}
function renderSearchResults(query, results) {
clearSearchResults();
updateSearchResults(query, results);
}
function clearSearchResults() {
const results = document.querySelector("#search-results");
while (results.firstChild) results.removeChild(results.firstChild);
}
function updateSearchResults(query, results) {
document.getElementById("results-query").innerHTML = query;
document.querySelector("#search-results").innerHTML = results
.map(
(hit) => `
<li class="search-result-item" data-score="${hit.score.toFixed(2)}">
<a href="${hit.href}" class="search-result-page-title">${createTitleBlurb(query, hit.title)}</a>
<p>${createSearchResultBlurb(query, hit.content)}</p>
</li>
`
)
.join("");
const searchResultListItems = document.querySelectorAll("#search-results li");
document.getElementById("results-count").innerHTML = searchResultListItems.length;
document.getElementById("results-count-text").innerHTML = searchResultListItems.length === 1 ? "result" : "results";
// searchResultListItems.forEach(
// (li) => (li.firstElementChild.style.color = getColorForSearchResult(li.dataset.score))
// );
}
function createTitleBlurb(query, title) {
const searchQueryRegex = new RegExp(createQueryStringRegex(query), "gmi");
return title.replace(
searchQueryRegex,
"<strong>$&</strong>"
)
}
function createSearchResultBlurb(query, pageContent) {
const searchQueryRegex = new RegExp(createQueryStringRegex(query), "gmi");
const searchQueryHits = Array.from(
pageContent.matchAll(searchQueryRegex),
(m) => m.index
);
const sentenceBoundaries = Array.from(
pageContent.matchAll(SENTENCE_BOUNDARY_REGEX),
(m) => m.index
);
let searchResultText = "";
let lastEndOfSentence = 0;
for (const hitLocation of searchQueryHits) {
if (hitLocation > lastEndOfSentence) {
for (let i = 0; i < sentenceBoundaries.length; i++) {
if (sentenceBoundaries[i] > hitLocation) {
const startOfSentence = i > 0 ? sentenceBoundaries[i - 1] + 1 : 0;
const endOfSentence = sentenceBoundaries[i];
lastEndOfSentence = endOfSentence;
parsedSentence = pageContent.slice(startOfSentence, endOfSentence).trim();
searchResultText += `${parsedSentence} ... `;
break;
}
}
}
const searchResultWords = tokenize(searchResultText);
const pageBreakers = searchResultWords.filter((word) => word.length > 50);
if (pageBreakers.length > 0) {
searchResultText = fixPageBreakers(searchResultText, pageBreakers);
}
if (searchResultWords.length >= MAX_SUMMARY_LENGTH) break;
}
return ellipsize(searchResultText, MAX_SUMMARY_LENGTH).replace(
searchQueryRegex,
"<strong>$&</strong>"
);
}
function createQueryStringRegex(query) {
const searchTerms = query.split(" ");
if (searchTerms.length == 1) {
return query;
}
query = "";
for (const term of searchTerms) {
query += `${term}|`;
}
query = query.slice(0, -1);
return `(${query})`;
}
function tokenize(input) {
const wordMatches = Array.from(input.matchAll(WORD_REGEX), (m) => m);
return wordMatches.map((m) => ({
word: m[0],
start: m.index,
end: m.index + m[0].length,
length: m[0].length,
}));
}
function fixPageBreakers(input, largeWords) {
largeWords.forEach((word) => {
const chunked = chunkify(word.word, 20);
input = input.replace(word.word, chunked);
});
return input;
}
function chunkify(input, chunkSize) {
let output = "";
let totalChunks = (input.length / chunkSize) | 0;
let lastChunkIsUneven = input.length % chunkSize > 0;
if (lastChunkIsUneven) {
totalChunks += 1;
}
for (let i = 0; i < totalChunks; i++) {
let start = i * chunkSize;
let end = start + chunkSize;
if (lastChunkIsUneven && i === totalChunks - 1) {
end = input.length;
}
output += input.slice(start, end) + " ";
}
return output;
}
function ellipsize(input, maxLength) {
const words = tokenize(input);
if (words.length <= maxLength) {
return input;
}
return input.slice(0, words[maxLength].end) + "...";
}
if (!String.prototype.matchAll) {
String.prototype.matchAll = function (regex) {
"use strict";
function ensureFlag(flags, flag) {
return flags.includes(flag) ? flags : flags + flag;
}
function* matchAll(str, regex) {
const localCopy = new RegExp(regex, ensureFlag(regex.flags, "g"));
let match;
while ((match = localCopy.exec(str))) {
match.index = localCopy.lastIndex - match[0].length;
yield match;
}
}
return matchAll(this, regex);
};
}

View file

@ -1,16 +0,0 @@
@import 'common/reset.scss';
@import 'common/variables.scss';
@import 'layouts/_default/baseof.scss';
@import 'layouts/partials/site-header.scss';
@import 'layouts/partials/nav-header.scss';
@import 'layouts/partials/site-footer.scss';
@import 'layouts/index.scss';
@import 'layouts/_default/list.scss';
@import 'layouts/partials/paginator.scss';
@import 'layouts/_default/single.scss';
@import 'layouts/search/list.scss';
@import 'layouts/partials/search/search-form.scss';

View file

@ -1,5 +0,0 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

View file

@ -1,24 +0,0 @@
html {
font-family: sans-serif;
--container-width: 75rem;
--pad-x: 1rem;
@media (min-width: 30rem) {
--pad-x: 2rem;
}
}
body {
min-height: 100vh;
display: flex;
flex-flow: column;
}
main {
flex-grow: 1;
}
.section {
padding-inline: var(--pad-x, 1rem);
padding-block: var(--pad-y, 0rem);
}
.container {
max-width: var(--container-width, 60rem);
margin: 0 auto;
}

View file

@ -1,5 +0,0 @@
main.index {
.pages {
padding-inline: var(--pad-x, 1rem);
}
}

View file

@ -1,74 +0,0 @@
.paginator {
display: flex;
flex-flow: column;
.list {
flex-grow: 1;
padding-block: 1em;
display: flex;
flex-flow: column;
gap: 1em;
.page {
width: 100%;
max-inline-size: 75ch;
a {
font-weight: bold;
}
span {
font-style: italic;
}
}
}
/* _internal/pagination.html */
.pagination {
list-style: none;
display: flex;
justify-content: space-between;
gap: 1em;
inline-size: fit-content;
max-inline-size: 100%;
}
.page-item {
&.disabled {
}
&.active {
font-weight: bold;
}
}
.page-link {
display: grid;
place-items: center;
font-size: 1em;
line-height: 1;
width: 3em;
height: 3em;
border-radius: 100em;
background: #eee;
text-decoration: none;
color: inherit;
transition: all 0.2s ease-in-out;
&:hover, &:active, &:focus {
background: #ace;
}
&[aria-label="First"],
&[aria-label="Previous"],
&[aria-label="Next"],
&[aria-label="Last"]
{
background: none;
&:hover, &:active, &:focus {
background: #ace;
}
}
}
.active .page-link {
background: #06f;
color: white;
}
.disabled .page-link {
background: none;
&:hover, &:active, &:focus {
background: none;
}
}
}

View file

@ -1,5 +0,0 @@
.site-footer {
hr {display: none;}
padding-inline: var(--pad-x, 1rem);
padding-block: var(--pad-y, 1rem);
}

View file

@ -1,26 +0,0 @@
.site-header {
padding-inline: var(--pad-x, 1rem);
padding-block: 1rem;
.container {
}
.hang-left {
}
}
.site-masthead {
text-decoration: none;
color: inherit;
&__image {
}
}
.site-title {
}
.scroll-margin {
}
#top {
--header-height: 4rem;
}

View file

@ -1 +0,0 @@
@import 'common.scss';

View file

@ -1 +0,0 @@
@import 'common.scss';

View file

@ -1,23 +0,0 @@
2017-11-12 13:43 | https://mastodon.social/@trwnh/98991746572627131
problem with diaspora*'s aspects (and by extension, Google+ circles which aped it) is that it's a bidirectional metaphor for a unidirectional relationship. you're supposed to pick who can see a post, but they might not even follow you. I would understand if instead it functioned and was advertised as a way to split your timeline into multiple timelines. As is, sharing to aspects/circles/etc. is needlessly confusing.
although having a way to tag/categorize your own toots could come in handy as well, if you post about multiple disparate topics. it's a nightmare to maintain one Twitter account per interest/community.
---
2017-12-17 09:55 | https://mastodon.social/@trwnh/99189033326279405
unrelated thought: been trying to hash out a solution to the "multiple accounts" issue with social media. namely: why/when do people use multiple accounts, and what features can be implemented to reduce the friction in using just one account? would appreciate feedback.
off the top of my head:
- private vs public accounts for trusted people (answered by privacy options on toots)
- multiple interests (not really solved currently; perhaps implementing a tag system and letting people mute certain tags? Diaspora*-style aspects can get complicated)
- separate identity (unsolvable and would be insecure to attempt with one account)
wrt multiple interests, this really is my biggest pain point with ANY social media (coming from someone who has had like 15 birdsite accounts at once)
perhaps not exactly tags, but having a category system like google+ would be nice and perhaps easiest to manage when considering tootboosts. but this also might complicate the issue? nevertheless, it could be optional (default behavior would be to boost to your profile, no categorization involved)
the tag approach would be easiest for your own toots, but the categories wouldn't be too bad and would also allow for separating different boosts

View file

@ -1,31 +0,0 @@
it's a bit weird that some fedi blocklists use language like "fence" because whenever i see that, i don't think "safety", i think "homeowners association", and i think the HOA characterization of certain blocklist efforts is actually not wrong. it is reminiscent of the abstract desire for "safety" rather than any sort of concrete safety or just treatment. "freedom of association" only goes so far to justify punitive and destructive actions at an instance-state level
of course, what is to be done? what is a more appropriate solution? i think whenever such blocklists are circulated, it usually represents a failure mode at an institutional level. the rise of blocktogether on twitter was due to the systemic failure of their moderation. earlier fedi users may or may not remember the infamous "wil wheaton blocklist" that imported from infamous terf randi harper, who billed her list as "99% gaters and mras" (and i guess 1% trans ppl?)
so i think the "question of the day" now, is, what is the failure of fedi and how could we have avoided it and how can we proceed to mitigate it.
for my part i think it comes down to the "instance" model. no one cares if you have to ban someone from a building, but evicting them from their house or expelling them from their country is disproportionate. so naturally, it becomes an issue when the "community" and "living space" are the same thing.
every day more and more it becomes clear we need to restructure around actual communities at a separate layer.
similar to the dynamics of a cliquey community instance, but without the expanded audience that federation brings. imagine just turning off federation.
maybe in some ways that's exactly what is going on -- clique communities are feeling threatened by the inherent lack of control. overblocking is in that vein similar to defensive centralization; after all, what is centralization if not simply blocking *all* remotes? and the balance is set by how much control they want to wrest back.
i think the "objectionable" part to me is that clique mentality. especially as it tries to assert itself over others. the other day i had someone ask me which instance to join and i realized for the first time in 6 or 7 years that i didn't have an answer. they just wanted to exist. they didn't want to have to care about border controls and digital embargoes. and they didn't really have *any* good options.
there isn't really any well-connected generalist instance that isn't overly blocked or doesn't overly block. much less an art-focused one. or a fandom-based one. so they stayed on tumblr and discord even though they *want* to join fedi. there just isn't anywhere for them to find a home in an area that is both good to live in *and* free of HOA type behavior. they don't want a gated community and they don't want a place with drive-by harassment. and they want to actually reach people.
> [I] suspect that the reaching people, the lack of harassment, and lack of blocking are kind of corners of a triangle.
yeah it certainly does seem like a trilemma
instance moderation is absolutely a huge job if you want to stay on top of it. it can be easier if you take a reactive rather than proactive stance. but the problem with proactive stances is that you now need to judge someone *before* they have harassed you. and the way people approach recommended blocks, they might not have been harassed either. you'll never know the difference unless you investigate for yourself... and that's hard rn.
now, if people just said "hey we dont like them, they failed the vibe check and we're not gonna spill any tears", that's more respectable than making something up. i can't count how many times i've seen people blocked for reasons that are patently absurd if you know the people. you use a reclaimed slur, you get blocked for using slurs. somewhere along the line that becomes "hate speech". a while ago it was "federates with the usual suspects". guilt by association morphed into "alt fedi"
the hard part is... they're not always wrong! a lot of blocks make sense when you look into them. but it's a mix, and the false positive rate is a bit too high imo. and there's no indication when you lose friends over your admin's decisions. and the policies aren't always visible, bc some admins are hiding them now to prevent scrapers tracking them. it just becomes this whole uncertain mess where you never know who blocks who and why.
---
my problem with oliphant is that there really should be a "tier negative one", given that the "tier 0" list doesn't have 100% consensus. my problem with thebadspace is that every single entry has the same tags, even where it makes no sense. my problem with blocklist culture in general is that there's zero accountability, review, or forgiveness built into them; often, they lack any sort of context, and if there is any reason given, the reasons are laughably flat-out ridiculous, incorrect, or inaccurate. i've seen blocks for being "channers" or "edgelord" being levelled against people who couldn't be any further than that. i've seen blocks for "no moderation" when no reports were filed. i've seen blocks for reasons such as "underage" or "reclaimed slurs" or vague unspecified "accusations". there's no differentiation between "hosts twitter crosspost bots" and "contains literal nazis that will send you death threats". fundamentally, i do not think that it is healthy to conflate safety with mere annoyance or a misalignment of "our" values. this is before you even get into the subjectivity of all such judgements... it is, of course, everyone's freedom of association to do whatever they want for themselves, but it is the point at which people start recommending or expecting that you do the same, that it then becomes a problem. and the real problem is not who you do or do not block. the problem is that there are no clear boundaries or clearly-established spaces or contexts for people's communications. you talk to someone that happens to be on someone else's shitlist, and you just might end up on that same shitlist for "federating with the usual suspects", where "the usual suspects" is an unbounded and growing set of people that seems to propagate further every time you look at it. it very frequently leads into policing who any given person mentions offhand or boosts in passing.

View file

@ -1,13 +0,0 @@
https://mastodon.social/@trwnh/110310774875374491
re: mastodon and matrix having "default servers" that end up being too big: i feel like if they were easier to selfhost it would be less of a problem. the "default server" paradigm is more effective when you can't easily set up other servers.
you ever try to host matrix? it's awful. like 2GB memory usage just for a single user in a big room.
mastodon is getting to be pretty similar, not because of the base software per se, but because of how many moving parts it has and how much resource usage accrues as a result. rails, postgres, redis, optional elasticsearch if you want working search, a nodejs streaming server and frontend... that's before you even get to the network traffic due to architectural decisions
sure, but you can at least block entire bad servers with little-to-no collateral damage. and you can also use allowlist federation or some other restriction mechanism to avoid fully open federation. just like avoiding fully open registrations.
i've oft thought about an opt-in federation of instances that require human approval for registrations. or a closed "federation" in the irc distributed sense.
it'll never happen because gargron wants more users, and approval-based signup makes a lot of disinterested people bounce before actually trying the software. but it's worth considering imo at least as a thought experiment

View file

@ -1,5 +0,0 @@
https://mastodon.social/@trwnh/109826683171183861
if/when i get around to building [untitled social app] i want to set some hard design expectations for it before even thinking about if/when/how to release it (and which parts of it). there's a lot of expectations i want to challenge, i don't want to take anything for granted. if anything, whatever i build will simply be the practical application of my grand unified communication theory. it's as much an experiment and learning process as it is a tool or utility.
which is to say: any code i write or software i build, it will not be the primary product. the primary product will be the documentation and reasoning behind it. it will be in researching existing protocols and ecosystems, then identifying how they map onto the theoretical patterns and structures. nail the recipe before baking the cake.

View file

@ -1,40 +0,0 @@
https://mastodon.social/@trwnh/109965906891374818
we have a domain name system but why don't we have a domain number system
just like we can register human-friendly names, we should be able to register persistent machine-friendly numbers to websites and domains. the name and number are aliased together, or we map name => number => ip address
yes this idea is copied from xri i-names and i-numbers, it's one of the best ideas to come out of that effort (aside from delegation of authority at any point in the identifier)
the world if xri took off
future.jpg
i've talked/thought about doing something similar with webfinger, but the "authority delegation" bit is really hard without a persistent unchanging identifier in some common authoritative namespace. you can defer to some other DNS domain's webfinger endpoint, but that doesn't get rid of the fragility in expiring or reassigning domain names.
it's interesting that bluesky comes to a similar conclusion with its "placeholder" DID scheme, did:plc: -- identifiers are centralized around a single "PLC server". the DNS stuff maps to a did:plc: that gets resolved by the centralized resolution server.
we could do something similar with a centralized server/domain that did nothing but assign identifiers and map to current location... of course, that doesn't solve the problem, it just shifts the burden.
so here's a crackpot idea that requires "minimal" changes to the way the world works
1. register a TLD with the IANA/ICANN -- let's call it something like .uuid
2. allow anyone to register UUIDs on this TLD -- this should be as easy as registering any other domain name, but instead of getting to pick the name, it's auto-assigned to you
3. CNAME your desired public-facing domain name to the .uuid domain you registered
4. optionally: run some software to "reverse map" your .uuid URIs (Webfinger)
you could also use plain old HTTP redirects instead of Webfinger, if you trusted the thing at the other end to stay online
but the main thing would be being able to use
trwnh.com/some-resource => 05517367-0a6a-42c1-9810-9fcf264a505b.uuid/some-resource
and then later change trwnh.com to some other domain
any software/application dealing with identifiers SHOULD support using the "canonical" identifier, so you can signal that the .uuid-assigned URI is the canonical one
crucially, the .uuid registry would never expire, and probably it should be free. the goal is to never let the .uuid registration lapse, ever
maybe your domain name registrar could even host a reverse-mapping server for you, if they wanted to expand their services?
also something to consider: under what circumstances might an entire TLD lapse? like this is super unlikely, sure, but what happens if the entity behind some TLD in the IANA root zone database goes insolvent?

View file

@ -1,8 +0,0 @@
actually it's kinda interesting how every single spec largely breaks down along one of three ideological lines: plaintext, xml, or json
i'm seeing a general analogy between DNS, XRI, and Webfinger resolution
- DNS returns a plaintext answer
- XRI returns an XRD
- Webfinger returns a JRD

View file

@ -1,83 +0,0 @@
is2g ifwhen i make my thing i am going to put a lot of effort into a filter system that actually makes sense. i will not be supporting "mute" or "block" as binary features, but rather, everything flows through the filter system. mastodon filter v2 system is an improvement but doesn't go far enough
---
in any case yes it would be better to encrypt everything or just not peer/replicate at all. fedi has a problem with this because for some unknown reason we all decided it would be a convoluted distributed state machine which is a bad idea when you don't trust all participants, and also worse when those participants are explicitly redistributing it while attributed to you. at that point you might as well go all-in on syndication rather than federation. put another way: federation makes sense for message passing but not for publishing (and fedi is an awkward and unclear hybrid between messaging and publishing)
---
idle thought: idk if i care so much about "fedi" as much as it is "people who are on fedi". either in the abstract or in the concrete, i just wanna make friends and talk to cool people. fedi is little more than a place for fateful encounters based on the streams of consciousness we put out there. the important thing is that no one meddles with this for profit or otherwise. the second fedi stops providing that, i'm out.
it's a big part of why i don't really relate or sympathize with people who just want fedi to grow. i think any growth has to be sustainable. the values are valuable. if the values are compromised then fedi isn't valuable to me anymore.
note that there's a big distinction between this and communicating with people you know. if some people are only on fedi then that has a different type of value. network value is not the same as the value of the network.
it's always the people, i'm only here for the people. i do not want to keep track of the diplomatic politics of tens of thousands of nation-states. no one should! we made a wrong turn somewhere back in 2017 when people started making "community" instances, and arguably we were on the wrong path from the very beginning. maybe the whole thing is wrong tbh. we need to go back to actual forums. the ergonomics are all wrong. the model is wrong. we're just trying to mash everything roughly into the shape of twitter. and worse, every twitter has its own culture and community and is assumed to be homogeneous! how do you atomize all this?
---
[asking natalie what should be removed or what is missing from misskey](https://mastodon.social/@trwnh/109958119755489250)
<blockquote>
i like that it's an outsider take on fedi and is not afraid to push boundaries and go beyond what mastodong has envisioned
what's missing is muting/blocking actually being functional. i'd also like to see Room come back lol
what should be removed: basically nothing. maybe proxy accounts. if i wanted minimal i'd go use some useless software like honk, misskey maximalism is actually really fun
</blockquote>
---
the general objection is "that's not how we currently federate", as if the way we currently federate should be preserved forever
---
"relays" on fedi are like the worst possible interpretation of relays. imagine one big irc room that has no topic whatsoever. just thousands of people posting whatever they want. why
also imagine your irc client had autologging turned on, and didn't have an easy way to clear specific logs
---
this shit should be generic and it can be browsed by a social web browser or a linked data renderer
i think it makes the most sense to lean into the Web and just focus on making the publishing as easy and straightforward as possible. i think most people (ideologues aside) don't actually want "federation" as commonly thought of. in absolute terms they want their posts to show up in a reader.
there still is a place for federation, the "federation" i'm interested in is more like replication in a distributed database. and i'd like to link it all up across domains like "nomadic identity".
example: imagine a network of generic servers
your server shuts down, you walk up to any server that has your posts already, prove your identity, pick up like nothing happened
basically you should be able to "claim" your federated profile on other domains, kind of like how on a centralized site you can claim your profile page if you're a celebrity or whatever
nomadic identity is literally just doing this process yourself beforehand by setting up mirrors/replicas of your key
---
i forget who it was that said "a single timeline is unsustainable" (aaron parecki?) but i'm feeling more like the real cardinal sin is publishing to a single profile. i don't mind *reading* everything in a single view, although making lists certainly helps. it's the *publishing* that annoys me. i almost *never* want to send a post to *all* of my followers. and this goes doubly for replies. i often want to reply within a specific context and only *optionally* tell my followers about it.
"one account per feed" is a goddamn travesty. an account should be able to have multiple profiles, and a profile should be able to have multiple feeds, and you should be able to follow individual feeds instead of being forced to follow an entire profile.
yeah, i was thinking similarly. except i was going to make Collections into actors that could be followed. which would be incompatible with mastodon, because mastodon assumes wrongly that actors must be exactly one of The Five Types. but then again, i am somewhat losing interest in this whole "social media" thing and would prefer to focus on a purer model of publishing web resources and managing them with collections. so maybe "mastodon compatibility" is not all it's cracked up to be...
if anything, i was thinking of treating "mastodon compatibility" similar to a crossposter up until such point that mastodon relaxes or removes its unnecessary type-checking. you'd have an actor that you can send your activities to, and it will translate them into mastodon-flavoured activitypub and deliver out that translated copy. this actor would have its preferredUsername and all that compat stuff.
---
btw i think that deletes being so costly is something that is entirely a consequence of having essentially replicated the data with a ttl of forever. that's the default mode of every "fedi" software. it makes no sense for most people and it stems from the ideological decisions of over a decade ago
by which i mean: there's a word for having your published content be replicated to other sites, and it's "syndication". we had this technology for really simple syndication... rss. it later evolved into atom. the "fediverse" was founded on ostatus built on atom feeds. when we say we're "federating" we're really more accurately "syndicating". you just don't have control over the accounts being created on other domains, on your behalf.
i keep talking about how the metaphors are wrong, like how we merged the concepts of publishing and messaging to the detriment of both. we're sending messages that contain the entirety of a resource that we meant to be published. of course it's going to lead to outcomes that don't make sense. maybe one day people will understand this
also... authorized fetch won't solve this. if you have a password protected blog, and someone is saving every new post and republishing it, and you let this happen continually, then what's the point of the password?
---
i think it's very important that users be able to understand and reason about the software that they are using! when it "does the thing", it should do so predictably.
the argument for generic AP is that generic AP allows you to manage resources predictably, without having to care what others will do. it's a standardized storage layer. without it, you need a mastodon account, a pixelfed account, etc... every "platform" owns your data separately.
imagine if switching from outlook to thunderbird necessitated losing all your emails and maybe even your contacts.

View file

@ -1,2 +0,0 @@
+++
+++

View file

@ -1,127 +0,0 @@
+++
title = "Dereferencing non-HTTPS URIs as `id`"
date = "2019-10-11"
+++
{{toc}}
## Preserved text
<https://socialhub.activitypub.rocks/t/dereferencing-non-https-uris-as-id/116>
---
@trwnh:
> 3.1 Object Identifiers
>
> All Objects in [ActivityStreams] should have unique global identifiers. ActivityPub extends this requirement; all objects distributed by the ActivityPub protocol MUST have unique global identifiers, unless they are intentionally transient […] These identifiers must fall into one of the following groups:
>
> 1. Publicly dereferencable URIs, such as HTTPS URIs, with their authority belonging to that of their originating server. (Publicly facing content SHOULD use HTTPS URIs).
> 2. An ID explicitly specified as the JSON null object, which implies an anonymous object (a part of its parent context)
>
> Identifiers MUST be provided for activities posted in server to server communication, unless the activity is intentionally transient. However, for client to server communication, a server receiving an object posted to the outbox with no specified id SHOULD allocate an object ID in the actors namespace and attach it to the posted object.
>
> All objects have the following properties:
>
> id
>
> The objects unique global identifier (unless the object is transient, in which case the id MAY be omitted).
>
> type
>
> The type of the object.
One thing I've wondered about is point 1 under 3.1 -- the id MUST be publicly dereferencable, with its authority belonging to the origin server... but it only SHOULD be https.
So, does this mean that there are other possible choices for a publicly dereferencable id with proper authority, that *isn't* HTTPS? I assume that the intention was to suggest using HTTPS over HTTP (e.g. section 3.2 assumes HTTP GET and content negotiation, as well as headers and 403/404 error codes; section 5.1/5.2 specifies HTTP POST to outbox/inbox; Section 6 uses MUST for making an HTTP POST to actor outboxes and 201 codes), but my curiosity is with other URI schemes entirely.
Perhaps one practical consideration (as there are various impractical ones, such as file://, ftp://, ftps://, sftp://, and so on) is to not use a URL but instead use a URN (such as doi, isbn, and other urn: URIs). Of course, this requires us to do a little more work to treat them as "dereferencable", such as by including a HTTPS proxy or some other resolver service. I wonder how that might be done, and whether this is at all worth pursuing. It could be used for deduplication, for example, by assigning a network-wide URN, with the authority deferred to the lookup service/proxy used as an instrument.
---
@cwebber:
> So, does this mean that there are other possible choices for a publicly dereferencable id with proper authority, that isnt HTTPS?
Yes and the [Golem demo](https://gitlab.com/spritely/golem/blob/master/README.org) shows exactly an example of this, using a (very-cut-down demo) of using [Datashards](https://datashards.net/) with ActivityPub (it was called “magenc” back then) to distribute activities. A similar example could be done with IPFS, for instance (though that doesnt provide the privacy/security properties that Datashards does).
Similarly, [bearcaps](https://github.com/cwebber/rwot9-prague/blob/bearcaps/topics-and-advance-readings/bearcaps.md) are a possible non-https URI scheme that we might use.
[...] Wanting to support Tor Onion Services is a reason I explicitly pushed back against pressure to make the spec https-only, which some people wanted.
---
@trwnh:
I guess what Im trying to comprehend the most would be, how would compatibility work between the HTTPS linked-data web, and the non-HTTPS documents? If we start passing around AP JSON-LD documents with non-HTTPS `id` then it would obviously break due to basically all implementations assuming that all they need to do is HTTP GET `id`.
I remember reading the [RWoT paper about DIDs in ActivityPub](https://github.com/WebOfTrustInfo/rwot5-boston/blob/master/final-documents/activitypub-decentralized-distributed.pdf) and having the same confusion at the time about how it would work practically. The closest thing I could find was [`instrument`](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-instrument) in the AS2 Vocab, but that has a domain of `Activity` only, and is described as "Identifies one or more objects used (or to be used) in the completion of an `Activity`", so it would have to be redefined to work on `Object` as well.
---
@rinpatch:
Wouldn't an array of `id` work?
---
@trwnh:
Actually, multiple `id` seems interesting to me for a different thing: nomadic identity. Consider a case where an Object has multiple HTTPS URIs.
That might not be the best way to do it, though. In fact, one shortcoming that is immediately apparent to me is the question of what to do if the document retrieved from one `id` is different from that retrieved from another `id`. So it would be better to have one canonical `id` only, where possible. But technically, the problem of differing documents can still happen, due to something as simple as a desync.
[[future errata: this is what `alsoKnownAs` could be used for?]]
Id really like to focus on modeling how to dereference non-HTTPS in a way that is still roughly compatible with the HTTPS Web-based network. So far the following options have been mentioned:
* `id` as Array; pick whichever one you understand.
* `url` as Array; pick whichever one you understand but leave `id` as HTTPS.
* just break compat and let implementations figure out how to resolve `id` (if they can)
* use a local proxy directly as `id` (technically still globally unique but represents change of authority)
* use a proxy Service as `instrument` (and extend `instrument` to be applied to `Object` and not just `Activity`)
---
@how:
Im a bit concerned about extending to objects what could be done with creating an object-specifc actor acting as a proxy. The `instrument` service seems to be adapted to this use-case: you get a representation of the objects metadata but must use the out of band service to retrieve the actual object. Best of both worlds?
---
@nightpool:
The ActivityPub standard does not define that any given server must support any one scheme. I dont understand what the purpose of having it do so would be? it would just limit the flexibility of the protocol for no practical benefit (If two servers dont support the same schemes, then theyre obviously not going to be able to talk to each other, MUST or no MUST)
---
@trwnh:
I think theres probably mixing of concerns to treat non-HTTPS as primarily an issue of nomadic content; while there is *potential* application toward this, I think the incompatibility issue is probably more pressing in the long run. You can claim that a software is “compatible with ActivityPub”, but this is a statement that needs clarification and qualification. And this is also something that would become even more of an issue if ActivityPub is implemented via networks other than HTTP(S).
In that sense, there is no “ActivityPub network” there is only currently an “ActivityPub over HTTPS network” (using HTTPS ids), as well as a sub-network of “ActivityPub over HTTPS network, with WebFinger” (perhaps more colloquially referred to as “the Mastodon network”).
Even if every (or the majority of) software implementation(s) moves to adopt `bear:` URIs as `id`, there is now still the question of which URI schemes are supported with the `u` parameter of the bearcap. The draft for bearcaps defines `u` as “The stable URL that the bearer capability URI is associated with for requests”, which is, again, not specifically HTTP(S). But even then, specifying a “stable URL” means that we are precluding URNs as a form of reference.
As nightpool mentions:
> If two servers dont support the same schemes, then theyre obviously not going to be able to talk to each other
So I guess my main concern is that so-called “ActivityPub” software will settle around the lowest common denominator or the most restrictive set of requirements for interoperability. Id like to lessen that if possible, so that individual implementations have the option to explore alternative lookup strategies for other “publicly dereferencable” schemes without that implying a choice between “hard break in network compatibility” or “stuck using HTTPS for `id` because everyone else is”. Although a hard break might be desirable in some cases, e.g. “if you dont understand bearcaps then you cant interact with this object”.
---
## current thoughts
we've well and truly collapsed into the "https only" world by now. any attempt to use a different scheme represents a sort of netsplit. this is not all good or bad though; it just means that the only network we have is the World Wide Web.
`alsoKnownAs` may be useful here as well: <https://socialhub.activitypub.rocks/t/defining-alsoknownas/907/30>
> i see a few possible approaches:
>
> 1. defer `alsoKnownAs` to mastodon's usage (concept 2, controlling the listed actor). define a new term `aliases` (pending name) to represent concept 1, different identifiers for the same subject. we might also define `subject` or some similar term to identify a "canonical" identifier?
>
> 1. defer `alsoKnownAs` to the DID core definition (concept 1, different identifiers for the same subject). implement a transitional period in which mastodon and other projects switch to a different mechanism more along the lines of rel-me. eventually deprecate the use of `alsoKnownAs` for determining rel-me.
aside from that, the `instrument` idea still has some potential

View file

@ -1,24 +0,0 @@
+++
title = "Notifying remote servers that they should refetch an object"
date = "2019-11-01"
+++
{{toc}}
## Preserved text
<https://socialhub.activitypub.rocks/t/exposing-edit-history-via-activitystreams/2930>
@trwnh:
Per [expose edit history in ActivityPub representation · Issue #23292 · mastodon/mastodon · GitHub ](https://github.com/mastodon/mastodon/issues/23292)
@Johann150 raises a feature request for being able to view earlier versions of an activity or object before an Update was received. In the case where an object is Created and then Updated, someone receiving the Update who didnt receive the Create first would have no way of knowing what was contained in the original version. Someone fetching the object without having received either of the activities would be able to know that it was edited based on the presence of the `updated` property, but likewise would not be able to retrieve any additional information about previous versions.
Looking at the existing Activity Vocabulary we see that the only existing way to do this would be in the case that `context` was published, although this requires that the context MUST be a Collection, and you would have to dig through all the items to filter for Update activities (plus the initial Create). Additional issues arise from the ordering of Collections not always being consistent, but in general you will still have to traverse the entire context collection.
Therefore it likely makes sense to propose a new property called something like `history` or `revisions`. If there is any prior art or some existing specifications that could apply here, it would be helpful to know about them.
## current thoughts
idk

View file

@ -1,436 +0,0 @@
+++
title = "Unresolved issues surrounding Follow activities"
date = "2019-10-10"
+++
{{<toc>}}
## Preserved text
<https://socialhub.activitypub.rocks/t/unresolved-issues-surrounding-follow-activities/114>
<article>
# First, some context…
What the C2S spec says:
> 6.5 Follow Activity
>
>
>
> The Follow activity is used to subscribe to the activities of another actor.
>
>
>
> The side effect of receiving this in an outbox is that the server SHOULD add the object to the actors following Collection when and only if an Accept activity is subsequently received with this Follow activity as its object.
What the S2S spec says:
> 7.5 Follow Activity
>
>
>
> The side effect of receiving this in an **inbox** is that the server *SHOULD* generate either an `Accept` or `Reject` activity with the Follow as the `object` and deliver it to the `actor` of the Follow. The `Accept` or `Reject` *MAY* be generated automatically, or *MAY* be the result of user input (possibly after some delay in which the user reviews). Servers *MAY* choose to not explicitly send a `Reject` in response to a `Follow` , though implementors ought to be aware that the server sending the request could be left in an intermediate state. For example, a server might not send a `Reject` to protect a users privacy.
>
>
>
> In the case of receiving an `Accept` referencing this `Follow` as the object, the server *SHOULD* add the `actor` to the object actors [Followers Collection ](https://www.w3.org/TR/activitypub/#followers). In the case of a `Reject` , the server *MUST NOT* add the actor to the object actors [Followers Collection ](https://www.w3.org/TR/activitypub/#followers).
> > Note
> >
> >
> >
> > Sometimes a successful `Follow` subscription may occur but at some future point delivery to the follower fails for an extended period of time. Implementations should be aware that there is no guarantee that actors on the network will remain reachable and should implement accordingly. For instance, if attempting to deliver to an actor for perhaps six months while the follower remains unreachable, it is reasonable that the delivering server remove the subscriber from the `followers` list. Timeframes and behavior for dealing with unreachable actors are left to the discretion of the delivering server.
Special collections:
> 5.3 Followers Collection
>
>
>
> Every [actor ](https://www.w3.org/TR/activitypub/#actors) *SHOULD* have a `followers` collection. This is a list of everyone who has sent a [Follow](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-follow) activity for the actor, added as a [side effect](https://www.w3.org/TR/activitypub/#follow-activity-outbox). This is where one would find a list of all the actors that are following the actor. The `followers` collection *MUST* be either an [ `OrderedCollection` ](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-orderedcollection) or a [ `Collection` ](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-collection) and *MAY* be filtered on privileges of an authenticated user or as appropriate when no authentication is given.
> > Note: Default for notification targeting
> >
> >
> >
> > The follow activity generally is a request to see the objects an actor creates. This makes the Followers collection an appropriate default target for [delivery](https://www.w3.org/TR/activitypub/#delivery) of notifications.
>
>
> 5.4 Following Collection
>
>
>
> Every actor *SHOULD* have a `following` collection. This is a list of everybody that the actor has followed, added as a [side effect](https://www.w3.org/TR/activitypub/#follow-activity-outbox). The `following` collection *MUST* be either an [ `OrderedCollection` ](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-orderedcollection) or a [ `Collection` ](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-collection) and *MAY* be filtered on privileges of an authenticated user or as appropriate when no authentication is given.
In summary… C2S Follow puts a Follow activity in your outbox, your server waits for an Accept, then adds that actor to following. S2S Follow takes a Follow activity in your inbox, your server wraps it in Accept/Reject and delivers that. S2S Accept Follow adds that actor to followers. (All of this is SHOULD language.)
However, this leaves some things too ambiguous…
# Following only a *subset* of activities
By default, when you Follow an Actor, most implementations interpret that as a sign of interest in every single activity that the actor publishes, similar to the note in 5.3 (“The follow activity generally is a request to see the objects an actor creates.”). But there is no way to signal an interest in receiving only *some* of the activities. Perhaps the only possible concession here is the `streams` property of an actor, but there is basically nothing to guide implementations aside from a passing mention that is only 10 words long:
> **streams**
> A list of supplementary Collections which may be of interest.
This suggests a pull-based approach, where you can GET the `streams` of an actor… but you cannot have the actor deliver any activities from those streams. Nor is it explicitly defined whether these collections contain activities or objects, or what the collections are actually for (aside from heuristically guessing based on the name of the collection?) Perhaps there could be a way to consistently understand these collections and maybe follow just those streams instead of the actors, but that would require…
# Following non-actor objects
Per 6.5, “The Follow activity is used to subscribe to the activities of another actor.” But sometimes you dont necessarily care what a single actor does with multiple objects, you care about what multiple actors are doing with a single object.
AFAIK some implementations interpret Follow Object to generally mean that the software will deliver any activities targeting that object to any actor that has expressed interest through the Follow Object activity. Perhaps this should be formalized, as the following are currently not agreed upon:
* Who should you send the Follow Object to? The `attributedTo` actor? Does following that object imply also following the actor?
* How should you keep track of followed objects? Should they be added to the `following` collection like actors? Should individual objects define a `followers` collection?
* How do you know if an object is followable? If you send a Follow Object to remote software, it may not understand it. Is the presence of a `followers` collection enough for this? Does it also need `attributedTo`? Some other method?
# Practical applications
As prior art, Google+ Collections come to mind. For each post made to Google+, a user could choose to address it to Public, or to selected users (like actors), or to a Circle (like a collection of actors), or to a Community (like a group), or to a Collection (like a category/stream). This could be seen as confusing since it wasnt really well-explained to users what any of these concepts meant, and Google+ also used Circles both for addressing those users *and* for viewing posts from those users, whereas most other services have a separate Lists feature for viewing posts from a subset of users. In essence, users can categorize their posts at the same time as they choose who to address them to. Consider the following hypothetical documents:
```
{
"id": "https://social.example/actors/1",
"type": "Person",
"inbox": "https://social.example/inboxes/1",
"outbox": "https://social.example/outboxes/1",
"followers": "https://social.example/actors/1/followers",
"streams": ["https://social.example/collections/1"]
}
```
```
{
"id": "https://social.example/objects/1",
"type": "Note",
"content": "a simple note",
"attributedTo": "https://social.example/actors/1",
"followers": "https://social.example/objects/1/followers"
}
```
```
{
"id": "https://social.example/collections/1",
"type": "OrderedCollection",
"items": ["https://social.example/objects/1"],
"followers": "https://social.example/collections/1/followers"
}
```
```
{
"id": "https://social.example/activities/1",
"type": ["Create", "Add"],
"actor": "https://social.example/actors/1",
"object": "https://social.example/objects/1",
"target": "https://social.example/collections/1",
"to":
[
"https://social.example/actors/1/followers",
"https://social.example/objects/1/followers",
"https://social.example/collections/1/followers"
]
,
"cc": ["as:Public"]
}
```
# Etc
Any comments, additions, etc. about this?
</article>
---
@trwnh:
I should also add some discarded approaches that I considered at one point while thinking about this but determined to be too problematic:
* interpreting a Follow as subscribing to multiple actors instead of just one
* multiple outboxes (although this might still be applicable to other problems, just not this one for now)
* using Profiles instead of Actors (though this could still be done, it would be unsupported in current implementations; it would however become possible to Follow Profile instead of Follow Actor)
* having objects just be made into actors, a la the concept of message passing in computer science (although this adds a lot of complexity because objects generally only need a small part of what Actor provides)
* hackily having multiple actors representing the same Person, but using them to create objects, then Announcing those from a more “central” actor (too complicated and unwieldy)
---
@trwnh:
Sure, but how should we discover this?
As stated above I went with `attributedTo` and the presence of a `followers` property, but perhaps it may be better to use an explicit endpoint like `inbox`?
---
@trwnh:
Its almost 3 years later and I think Ive come around on this a bit
> As stated above I went with `attributedTo` and the presence of a `followers` property, but perhaps it may be better to use an explicit endpoint like `inbox`?
I think explicit is better than implicit. All thats required for an Object to be an Actor is to have `inbox` and `outbox`. The only issue with current language in the spec is that
> The outbox stream contains activities the user has published,
and semantically speaking actors are not equivalent to users, nor is the attribution required to match the same actor (or even *be* an actor `attributedTo` can include another activity, for example).
Where the previous approach fails is that the `followers` collection may not be included or published. The `followers` property is only a SHOULD and not a MUST.
For delivery of the Follow activity, we can refer to the guidance on delivery:
> The [inbox](https://www.w3.org/TR/activitypub/#inbox) is determined by first [ retrieving the target actors JSON-LD representation](https://www.w3.org/TR/activitypub/#retrieving-objects) and then looking up the `inbox` property. If a recipient is a `Collection` or `OrderedCollection`, then the server *MUST* dereference the collection (with the users credentials) and discover inboxes for each item in the collection. Servers *MUST* limit the number of layers of indirections through collections which will be performed, which *MAY* be one.
Which, again, implies that we should just attach `inbox` on anything followable. And if we do that, then we should/must attach `outbox` as well to make it an actor (although in practice this may not be strictly required it is possible to model having an inbox but no outbox, and some implementations might do this already in apparent violation of the spec).
---
@macgirvin:
I do recall when Mastodon first implemented ActivityPub and some folks were still using OStatus. In this case the inbox was set to NULL. We implemented AP at exactly the same time and this threw exceptions in my code. Was going to file a bug, but technically speaking I think its spec compliant. The language states that an actor must have the “following properties” and these records had the required properties, except they werent de-reference-able. In any case I consider this to be a legal interpretation and have code to handle it appropriately.
A more serious issue is that we implement access permissions to several collections and these are not de-reference-able unless the requestor has permission to access the collection. The spec is incredibly weak where it comes to dealing with permissions, but I have every right to restrict access of a resource to anybody I desire - even if it means my software isnt technically compliant. This makes the quoted section in your reply contentious. These are deliverable through the forward-to-owner sections referenced elsewhere in the spec so everything still works; but not all collections are de-reference-able by every actor that receives an activity — nor should they be.
---
@trwnh:
[quote="macgirvin, post:8, topic:114"]
In this case the inbox was set to NULL.
[/quote]
Hmm. My interpretation is that `inbox` and `outbox` properties MUST be provided, but not necessarily be public. Im not sure whether it makes sense to set `inbox` to `null` explicitly; is that not the same as excluding it? In any case, the practical implication of the current network (“ActivityPub over HTTPS”, or as Ive taken to calling it recently, “ActivityPub over HTTPS with Webfinger and HTTP Signatures” :stuck_out_tongue: ) is that one can simply POST to an inbox IRI and have it either return successfully or error out with a 405 Not Allowed (if federation is not implemented). Id be curious to hear any objections or concerns regarding this.
[quote="macgirvin, post:8, topic:114"]
I have every right to restrict access of a resource to anybody I desire - even if it means my software isnt technically compliant.
[/quote]
Im not sure if its technically “incompliant” to do so, but for what its worth, Pixelfed for example provides `inbox` for actors (currently equivalent to users, but under this proposal, it doesnt have to be), and this `inbox` only supports POST and not GET, for obvious reasons federation works by POSTing to the `inbox` and GET is only needed for C2S fetching your own inbox with authorization. That doesnt seem like a problem to me; if anything, it seems natural since the inbox is only for delivery. Setting `inbox: null` or leaving it out implies that the object is not an actor, since it does not have both `inbox` and `outbox` (the only two required properties for actors). Aside from this heuristic, there doesnt seem to be any other meaning to being an actor. So I would agree that yes, access to resources may be restricted without authorization. This doesnt seem like an issue, although I may be misunderstanding or overlooking something.
[quote="macgirvin, post:8, topic:114"]
This makes the quoted section in your reply contentious. These are deliverable through the forward-to-owner sections referenced elsewhere in the spec so everything still works; but not all collections are de-reference-able by every actor that receives an activity — nor should they be.
[/quote]
Inbox forwarding could perhaps be applicable, but not any more so than it already is.
To clarify, the assumed / proposed flow is something like this:
1. I GET an actor at `https://example.social/actors/1` and see that their `streams` advertises `https://example.social/collections/1`:
```
GET https://example.social/actors/1 HTTP/1.1
Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"
{
"@context": [...],
"id": "https://example.social/actors/1",
"type": "Person",
"inbox": "https://example.social/inboxes/1",
"outbox": "https://example.social/outboxes/1",
"streams": ["https://example.social/collections/1"],
...
}
```
2. I want to `Follow` the `Collection` at `https://example.social/collections/1` for updates. (Lets say this `Collection` is advertised within `streams` and represents some categorization of posts, similar to Wordpress categories or Google+ Collections. Its items contain some arbitrary objects like `Note`s that have been published within the category/stream.)
3. I perform `GET https://example.social/collections/1`, optionally with an HTTP Signature for my actor `https://ap.trwnh.com/actors/1`. The server responds with a `Collection` containing only the items I am authorized to see, based on the HTTP Signature attached. If no signature is attached, then only items with `audience: as:Public` will be returned (likewise for `to`/`cc`/etc if the server software uses those for addressing instead of `audience`).
```
GET https://example.social/collections/1 HTTP/1.1
Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"
Signature: keyId="https://ap.trwnh.com/actors/1/publicKey",...
{
"@context": [...],
"id": "https://example.social/collections/1",
"type": "OrderedCollection",
"totalItems": 1312,
"first": "https://example.social/collections/1?page=1",
"last": "https://example.social/collections/1?page=132",
"inbox": "https://example.social/inboxes/1",
"outbox": "https://example.social/outboxes/2"
}
```
4. From this response, I parse for the `Collection`s `inbox` property. If it does not have one, I assume it cannot be followed. If it does have one, then I assume I can send a `Follow` there. In this example, `https://example.social/collections/1` has `inbox: https://example.social/inboxes/1`. (This happens to be the inbox for `https://example.social/actors/1`, but it doesnt have to be. This is an implementation detail.) To follow the stream for updates:
```
POST https://example.social/inboxes/1 HTTP/1.1
Content-Type: application/ld+json; profile="https://www.w3.org/ns/activitystreams"
{
"@context": [...],
"type": "Follow",
"actor": "https://ap.trwnh.com/actors/1",
"object": "https://example.social/collections/1"
}
```
5. The remote server then responds as per the individual implementations logic, e.g. with an `Accept Follow`
This is of course open to different methods of access control, object capability delegation, etc. the key point is that the followable object has an `inbox` to `POST` a `Follow`, which I prefer over heuristics like dereferencing `attributedTo` and looking for an `inbox` there. It is simpler to just provide the `inbox` directly. And you might as well provide an `outbox` too and make it a full-fledged actor, since being an actor doesnt actually imply much. Neither `inbox` nor `outbox` has to be public, anyway they just have to be there.
One other addendum is that, last I looked into this, I saw some discussion around Friendica and Zap having or planning support for `Follow Note`, but I was not able to find how this support was implemented, if it was.
---
@macgirvin:
Interesting. Yeah, that all make sense.
As I recall the rationale for inbox = null was a hint that an actor exists at this address, but it is only useful by some other protocol from ActivityPub. If you perform some alternate or broader scoped discovery (outside of AP) you might find that you can actually communicate with it.
We perform Follow Note and if I recall Ignore Note as internal activities to indicate you want to see (or not see) notifications for this thread in the future. We currently dont federate these activities - theyre strictly for internal use. The object is always the top-level post in a thread as these platforms default to conversation/threaded display.
Oh and the comment about permissions wasnt really about inbox/outbox but about recipient collections. We permit people to hide who is in their followers/following and circles/aspects collections if they dont feel like sharing these with the world. These will return 403 if the owner doesnt want anybody to see their contents. This affects the address de-duplication sections of the spec since these collections can only be expanded/iterated by their origin server. We do filter outbox contents based on the request http-signature but I think this is common practice.
---
@trwnh:
This may be off-topic but its worth pointing out that most projects Im familiar with tend to respond with some valid JSON(-LD), but intentionally leaving out any details the author does not want visible.
* Mastodons “Hide your network” option will return an `OrderedCollection` with the only useful property being `totalItems`. This is because Mastodons philosophy is to hide only the contents of the collection while still revealing metadata (presumably out of some concern for signaling “trustworthiness”, which to me seems flawed, but I digress):
```
GET https://mastodon.social/users/trwnh/followers HTTP/1.1
Accept: application/activity+json
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://mastodon.social/users/trwnh/followers",
"type": "OrderedCollection",
"totalItems": 2482
}
```
* Pixelfed currently does something similar, but intentionally “lies” and claims that there are 0 items while explicitly returning an empty array as the value:
```
GET https://pixelfed.social/users/trwnh/followers HTTP/1.1
Accept: application/activity+json
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://pixelfed.social/users/trwnh/followers",
"type": "OrderedCollectionPage",
"totalItems": 0,
"orderedItems": []
}
```
* Pleroma has separate toggles for hiding either the items or the count (or both). If the items are hidden, the `orderedItems` will simply not be present. If the count is hidden, the `totalItems` will simply not be present. If both are hidden, both properties will simply not be present. On a UX level, hiding the counts requires hiding the items, because hiding only the count leaves you open to manually counting the items yourself.
```
GET https://letsallovela.in/users/at/followers HTTP/1.1
Accept: application/activity+json
{
"@context": [...],
"first": "https://letsalllovela.in/users/at/followers?page=1",
"id": "https://letsalllovela.in/users/at/followers",
"type": "OrderedCollection"
}
```
* Misskey does the same thing as what you described: 403 Forbidden.
---
@trwnh:
In any case, I dont think that the spec necessarily intended to have the *sender* dereference follower collections for other actors rather, that role falls on the *receiver*, and the logic/rationale is as described in the Inbox Forwarding section, “avoiding ghost replies”:
> When Activities are received in the [inbox](https://www.w3.org/TR/activitypub/#inbox), the server needs to forward these to recipients that the origin was unable to deliver them to. To do this, the server *MUST* target and [deliver](https://www.w3.org/TR/activitypub/#delivery) to the values of `to`, `cc`, and/or `audience` if and only if all of the following are true:
>
> * This is the first time the server has seen this Activity.
> * The values of `to`, `cc`, and/or `audience` contain a Collection **owned by the server**.
> * The values of `inReplyTo`, `object`, `target` and/or `tag` are objects **owned by the server**. The server *SHOULD* recurse through these values to look for linked objects **owned by the server**, and *SHOULD* set a maximum limit for recursion (ie. the point at which the thread is so deep the recipients followers may not mind if they are no longer getting updates that dont directly involve the recipient). The server *MUST* **only target the values of `to`, `cc`, and/or `audience` on the original object** being forwarded, and **not pick up any new addressees** whilst recursing through the linked objects (in case these addressees were purposefully amended by or via the client).
>
> The server *MAY* filter its delivery targets according to implementation-specific rules (for example, spam filtering).
Essentially, inbox forwarding is a failsafe that allows a sender to say “look, heres the Activity, I dont know what this collection contains, so you deal with it, since its ostensibly under your authority.” This is why the “owned by the server” language is used multiple times in the above quoted section. Replying to, tagging, or performing an activity with the (locally owned) actor should trigger some discovery behavior. I must admit I find this recursive discovery behavior a bit problematic, although it is easier to swallow if you take it to mean “remote server implementations may notify / deliver to local actors that you reply to, tag, or act upon”. Perhaps it makes sense to notify someone if they were tagged or replied to, even if they were not explicitly addressed. Perhaps the implication is that they should be careful while doing so to not expand the scope of delivery. Perhaps the language in this third bullet point is simply bad language or bad guidance.
With that said, what matters for the purpose of inbox forwarding is that, if you are delivering to someone elses followers collection, then the sender needs to know how to refer to the `followers` IRI. But `followers` as a property is only a SHOULD and not a MUST. And I guess the “spam filtering” clause above also opens the door to implementations simply choosing *not* to forward to someone elses followers. (I think Mastodon does indeed filter out activities delivered to someone elses followers collection, based on a friends testing, but I cant remember for sure.) Anyway, a server that only implements S2S and not C2S might as well leave out the `followers` and `following` entirely, for privacy purposes. I dont think this would break anything if it does, that points to bugs in existing implementations. (Okay, it might break sharedInbox.)
One other tangent about inbox forwarding and addressing in general, that I might follow up on in another topic: I wish implementations would use `audience` more. It should be standard behavior to comment on someones post and address them, plus the value of `audience` (plus any other arbitrarily mentioned/added people, plus maybe your own followers). The audience of a post should be used even above `to`/`cc`, because to me `audience` carries human semantics while `to`/`cc`/`bto`/`bcc` are for server communication. For S2S purposes, the server implementation is actually free to deliver several slightly different activities to different inboxes, and in the case of `bto` and `bcc` this differing behavior is actually required. This, combined with the previous paragraph, leads to an issue where one may target their own `followers` collection without actually advertising it as their `followers` collection. Im not sure what kind of undefined behavior that would lead to… its things like this that make me think sharedInbox was a mistake.
---
@macgirvin:
This whole clause is about “conversational delivery” ; which is completely different from microblog use. We use conversational delivery so I understand it very well. This is the two fediverses problem I speak about quite often.
Ill put it in very simple terms. We have the Twitter fediverse and the Facebook fediverse. The protocol supports the Facebook fediverse through inbox forwarding but Twitter-like projects dont understand it and do things that break the Facebook fediverse. We can get along together but the first step is acknowledging that both models exist and learning how to implement both. The Facebook fediverse is important for things like private groups (ok “restricted”) and circles (restricted collections), so Twitter-like projects can get some benefit from supporting both.
And we can implement both. In the Facebook fediverse all comments to a restricted (not public) post go back to the sender of the top-level post and to nobody else. The actor that sent the top-level post delivers the comments downstream to all of the original recipients of the top-level post - and to nobody else. If its a public conversation commenters can send to whoever they want. This is the secret sauce that makes restricted conversations work.
In this model the sender of the top-level post is the only delivery authority for the entire threaded conversation. Now realise that this section was added to ActivityPub so that Facebook-like projects such as Friendica, Diaspora, and Hubzilla could exist in an ActivityPub fediverse, but the people writing the spec were nearly all from the Twitter side of the fediverse and to this day the Twitter side doesnt recognise that the Facebook side exists and has a different addressing/delivery model than they do.
It seems from your reply that conversational delivery is a bit alien to you as well. No worries. Ive proposed a mechanism to let everybody know for any given conversation which model to use. Its pretty simple and uses replyTo and works the same as emails reply-to. If its set, thats who you reply to - and you dont cc or deliver to anybody else; and you also copy that replyTo to your own comment before sending. If it isnt set, send your comment to whoever you want. Twitter-like projects will never set it except maybe in circles/aspects or private/restricted groups. Facebook-like projects will probably always set it except maybe in public conversations when their posts benefit from exponentially widening reach. And everybody lives happily ever after in the same fediverse. We support this today.
---
@trwnh:
Small point of contention: “Twitter fedi” projects like Mastodon implement inbox forwarding, but they dont make it the main mechanism of delivering replies in the same way that “Facebook fedi” projects expect to redistribute comments.
I am familiar with that model, and in fact Dennis Schubert proposed it some years back in criticism of the “twitter fedi” approach [ActivityPub - one protocol to rule them all? - Dennis Schubert ](https://overengineer.dev/blog/2018/02/01/activitypub-one-protocol-to-rule-them-all.html#replies-and-interactions) proposed back in 2018 that “Alices host is the one who delivered the shareable, so it feels somewhat natural to also ask Alices host to distribute the interactions […] a more sensible, reliable, and even more AcitivityStreamy way of handling replies would probably be adding the interaction to the replies collection and sending an `update`”. Whether the original activity is forwarded or whether an Update is sent doesnt particularly matter to me, since the model works the same either way your interaction exists in context of some top-level object.
In fact, we could be more explicit with this model by using the `context` property (which sadly goes largely unused on “twitter fedi”). If a post has a `context` of another post, it is a comment (Facebook-style). Otherwise, it is just a post (Twitter-style). These (Twitter-style) posts can exist in context of a Collection representing a conversation, or they can exist in a null context.
> Ive proposed a mechanism to let everybody know for any given conversation which model to use. Its pretty simple and uses replyTo and works the same as emails reply-to. If its set, thats who you reply to - and you dont cc or deliver to anybody else; and you also copy that replyTo to your own comment before sending. If it isnt set, send your comment to whoever you want.
Hmm, I guess this makes sense as a mechanism its like a more explicit version of `context`. Prior to this conversation I would have probably proposed something like the following algorithm:
1. Try to resolve `context` and check for `attributedTo`.
2. Resolve everything in `attributedTo` and deliver to any `inbox` found.
3. If there was no `context.attributedTo` or no actors/inboxes could be found, or if there was no `context`, then deliver your reply to whomever (your followers, as:Public, etc).
Again, though, Im not sure if `context.attributedTo.inbox` is the best approach here. At least `replyTo.inbox` saves a lookup step, and allows delivering to different actors, which would allow modeling anonymous objects.
As far as `context` goes, though, it would probably be a good idea to assign `context` = the top-level post (Facebook-style) or `context` = some Collection representing the conversation or thread, and then copy the `context` as-is when replying/commenting.
Perhaps an algorithm which supports both `context` and `replyTo` would look like this in pseudocode:
```
# Determine to/cc addressing
if "replyTo" in activity:
to = [
actor["inbox"]
for actor in activity["replyTo"]
if "inbox" in actor
]
elif (
"context" in activity
and "attributedTo" in activity["context"]
):
to = [
actor["inbox"]
for actor in activity["context"]["attributedTo"]
if "inbox" in actor
]
else:
to = ACTOR["followers"] + PUBLIC
# Copy any values that should be copied
if "replyTo" in activity:
replyTo = activity["replyTo"]
if "context" in activity:
context = activity["context"]
```
---
@macgirvin:
We also use context as youve described - but recently a number of new projects have started using context for other things than conversation ids. Fetching our context in fact returns a collection of the entire threaded conversation as seen by the root node - which is something I also wish more projects supported. But now that folks are doing other things with context and we also still have folks using them with OStatus semantics (which arent de-reference-able) I suppose well eventually have to come up with a different cross-platform way of fetching the entire conversation. Oh well.
Cheers.
## current thoughts
i plan to synthesize several things here -- a collection that could also be an actor, and maybe set as context. see FEP-7888 and a future FEP for that i guess

View file

@ -1,159 +0,0 @@
+++
title = "Resolving the Note vs. Article distinction"
date = "2019-11-01"
+++
## preserved text
<article>
# Background
[Activity Vocabulary - 3.3 Object Types ](https://www.w3.org/TR/activitystreams-vocabulary/#object-types):
> **Note**: Represents a short written work typically less than a single paragraph in length.
> **Article**: represents any kind of multi-paragraph written work.
Example 48 (Article):
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Article",
"name": "What a Crazy Day I Had",
"content": "<div>... you will never believe ...</div>",
"attributedTo": "http://sally.example.org"
}
```
Example 53 (Note):
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Note",
"name": "A Word of Warning",
"content": "Looks like it is going to rain today. Bring an umbrella!"
}
```
Semantically, the difference is never explicitly defined (how do you define a “paragraph”?), so the current fediverse has sort of assumed Article should be viewed natively on the remote website, while Note can be displayed as an inline status. Thus, Note is used to represent a status update, and a lot of the network just defaults to Note. The distinction is assumed to be formatting, but once again this is not an explicit definition (how do you define “formatting”?)
# Disambiguation
Going purely from the Activity Vocabulary descriptions and examples, I would possibly assume one or both of the following:
* Note SHOULD be plain text, Article SHOULD use HTML (or should these be a MUST?)
* Note SHOULD NOT use newlines (but are technically allowed to do so)
However, there is [ActivityPub 3.3 ](https://www.w3.org/TR/activitypub/#source-property), Example 8:
```
{
"@context": ["https://www.w3.org/ns/activitystreams",
{"@language": "en"}],
"type": "Note",
"id": "http://postparty.example/p/2415",
"content": "<p>I <em>really</em> like strawberries!</p>",
"source": {
"content": "I *really* like strawberries!",
"mediaType": "text/markdown"}
}
```
This example Note uses HTML for its `content`, in order to demonstrate the `source` property.
Also, ActivityPub Example 4:
```
{"@context": "https://www.w3.org/ns/activitystreams",
"type": "Create",
"id": "https://chatty.example/ben/p/51086",
"to": ["https://social.example/alyssa/"],
"actor": "https://chatty.example/ben/",
"object": {"type": "Note",
"id": "https://chatty.example/ben/p/51085",
"attributedTo": "https://chatty.example/ben/",
"to": ["https://social.example/alyssa/"],
"inReplyTo": "https://social.example/alyssa/posts/49e2d03d-b53a-4c4c-a95c-94a6abf45a19",
"content": "<p>Argh, yeah, sorry, I'll get it back to you tomorrow.</p>
<p>I was reviewing the section on register machines,
since it's been a while since I wrote one.</p>"}}
```
This example Note uses two `<p>` elements, representing two short paragraphs (once again not “less than a single paragraph”).
So even the specs themselves are inconsistent on any distinction.
# How much does this actually matter?
Arguably not much, since implementations often convert Note and Article into their own internal schema for statuses anyway. But it could still be beneficial to set a clearer distinction going forward on how these types should be assigned, ideally.
</article>
---
@lanodan:
The distinction I make between Article and Note isnt related directly to its content but on how its supposed to be presented and used, Articles are more things for blogs where you have about a post per day and so articles should be easy to find back with a list of articles/tags and maybe a bit of search features, Notes are more stuff like microblogging where you can easily have hundreds in a day and arent that easy to find back even with good keywords in full text search.
Also I find that formatting is actually very useful for notes because it allows to express more/equivalent with less (like a word-list vs a paragraph).
This question goes a lot in the fediverse because they are the two mainly used activity types but one could also ask about the actor distinction between Organisation and Group, Application and Service. And so far Ive only seen ActivityPub Document be used in the wild for Images with textual description (like if Image couldnt have it in the first place), but Document has no inherent meaning either.
Note: Pleroma keeps the distinction between Articles and Notes internally, no real differencies for the Mastodon API though but there could be a query filter.
---
@trwnh:
For prior art, I can think of semantic HTMLs `<article>` being a section of HTML that can be reproduced elsewhere in its entirety.
W3Schools: "independent, self-contained content. An article should make sense on its own and it should be possible to distribute it independently from the rest of the site."
MDN: "a self-contained composition in a document, page, application, or site, which is intended to be independently distributable or reusable e.g. in syndication."
This could be a distinction worth making, maybe? that an Article should roughly map to an `<article>`, whereas a Note is just arbitrary text?
---
@nightpool:
I think using `Article` is a perfectly fine solution for when the desired semantics are “I think its better in a short-form microblogging context to display a link to this thing rather then the full content itself”.
When considering that some clients (especially native clients) often wont support increasingly rich text formatting, I think using Article vs Note for content where you think plain-text fallback is unacceptable is a totally fine decision to make. (But you shouldnt ignore the fact that its probably likely that people are going to write long-form clients that *do* support Articles but dont support every possible type of rich text formatting, like the way RSS readers restrict allowed formatting for readability)
---
@trwnh:
I cant remember where this was said to me, but I think someone (maybe nightpool?) suggested that Note could be used for message passing a la Discord, which would be a third distinction and perhaps better than length/newlines or plaintext (as explained previously why those are inconsistent). So in that case, we could say Note vs. Article is one of *formality*. IMO this could complement the Article-to-`<article>` suggestion above.
Taken together:
* Note = for informal message-passing
* Article = for publication or syndication
The only thing unresolved with this distinction would be the blurry line that is microblogging. Microblogs are often both informal *and* intended for republishing in feeds. Itd be pretty clear-cut to use Note for a messaging application and Article for a blogging platform, but… perhaps this implies top-level microblogging posts should be an `Article` and replies should be `Note`? Or that they should all be `Article` regardless of the fact that they are informal? Theres still room for discussion there.
---
@darius:
I think this is right on the money. My main use case for Article is that its a way to say that “this is a publication” Ive often talked about how this might actually help solve the “quote-tweet” design problem in Mastodon (and presumably other software). If an Article is something that is formal and published then its also something that can reasonably be commented upon. Articles can be quoted-commented, and Notes cannot. So I can dunk on a New York Times article, but not on a random thing someone posted as a note.
Edited to add: for composing posts on a microblogging service, I wonder if a “people can quote-comment on this post” checkbox or whatnot could be, at its most basic, a switch that underneath it all changes type for your post from Note to Article.
---
@trwnh:
quoting and commenting is kind of out-of-scope and its up to each platform to decide how they want to handle it, tbh. from a data view, it doesnt really matter because you can put whatever you want in `content`, and `inReplyTo` is just metadata (like `tag`). You can have an Article inReplyTo another Article, just as you can have a Note inReplyTo another Note (or a Note inReplyTo an Article, or even vice-versa, etc etc.)
the thing is, though, that its really seeming like if we use “syndication” as the distinction, then that would imply a lot of things that are currently Note might be better conceived of as Article.
---
## current thoughts
i wrote a whole thing abt this [here](https://trwnh.com/wiki/tech/spec/activitypub/confusion/note-vs-article/)

View file

@ -1,60 +0,0 @@
+++
title = "Notifying remote servers that they should refetch an object"
date = "2019-11-01"
+++
{{toc}}
## Preserved text
<https://socialhub.activitypub.rocks/t/notifying-remote-servers-that-they-should-refetch-an-object/259>
@trwnh:
[quote="trwnh, post:1, topic:259, full:true"]
S2S Update:
> 7.3 Update Activity
>
> For server to server interactions, an `Update` activity means that the receiving server *SHOULD* update its copy of the `object` of the same `id` to the copy supplied in the `Update` activity. Unlike the [ client to server handling of the Update activity ](https://www.w3.org/TR/activitypub/#update-activity-outbox), this is not a partial update but a complete replacement of the object.
>
> The receiving server *MUST* take care to be sure that the `Update` is authorized to modify its `object` . At minimum, this may be done by ensuring that the `Update` and its `object` are of same origin.
Presumably sending something like this over S2S would just get the object replaced by an empty copy:
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://example.com/activities/1",
"type": "Update",
"actor": "https://example.com/actors/1",
"object": "https://example.com/objects/1"
}
```
So how would this update/refetch notification be done properly?
[/quote]
---
@nightpool:
wait, what? why would the example posted not be compliant? The two representations (including an IRI to dereference vs including an embedded object) should be identical on the JSON-LD level.
I agree that the spec might expect you to inline the object, but I absolutely disagree that it requires it.
---
@kaniini:
I agree with nightpool, a bare IRI is completely fine here.
---
@trwnh:
OK, then. If a bare IRI is fine, then the next question would probably be how this would be handled in existing implementations. If an ActivityPub implementation were to hypothetically use Update in this manner, would it be received and handled properly?
## Current thoughts
needs testing or review, but should be possible imo. and if it's possible then maybe it could be expected?

View file

@ -1,82 +0,0 @@
+++
title = "Signaling side effects asynchronously by generalizing Accept/Reject"
date = "2019-10-11"
+++
{{toc}}
## Preserved text
<https://socialhub.activitypub.rocks/t/signaling-side-effects-asynchronously-by-generalizing-accept-reject/125>
<article>
# Background
Per S2S (since Accept/Reject are not described in C2S):
> 7.6 Accept Activity
>
> The side effect of receiving this in an **inbox** is determined by the type of the `object` received, and it is possible to accept types not described in this document (for example, an `Offer` ).
>
> If the `object` of an `Accept` received to an **inbox** is a `Follow` activity previously sent by the receiver, the server *SHOULD* add the `actor` to the receivers [Following Collection](https://www.w3.org/TR/activitypub/#following).
>
> 7.7 Reject Activity
>
> The side effect of receiving this in an **inbox** is determined by the type of the `object` received, and it is possible to reject types not described in this document (for example, an `Offer` ).
>
>
>
> If the `object` of a `Reject` received to an **inbox** is a `Follow` activity previously sent by the receiver, this means the recipient did not approve the `Follow` request. The server *MUST NOT* add the `actor` to the receivers [Following Collection](https://www.w3.org/TR/activitypub/#following).
Note that for S2S purposes, Accept/Reject is only really defined for Follow activities. Per the Activity Vocabulary, it is also implicitly appropriate for `Offer` and `Invite`, but also [Example 10](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-accept) shows a much more ambiguous Accept Person targeting Group, which seems semantically awkward.
# Possible generalizations
Currently, the majority of the network uses Accept/Reject only for Follow on an Actor, and thus Accept/Reject is used only in that narrow definition. But there are other Activities that might require these semantics…
## Accept/Reject Join
Closed-membership `Group` semantics is maybe the most obvious benefit of generalizing Accept/Reject. It is of course possible to both Follow and Join a Group; Follow Group would semantically translate to being interested in what the Group posts to its outbox, as per the base AP spec. Some softwares handle Group membership based on its `followers`, but this is a bit of a hacky way to do things that is motivated by compatibility with the existing “Follow Actor” network. A much more semantic way to handle things would be to allow Join/Leave Group, but then Join would need Accept/Reject to allow modeling closed groups (at least, in a way different than just using the `Group` `followers` with `manuallyRequiresApproval` + `Announce` any activities received at `inbox` which is still a valid way of doing things for “mailing list” use cases). This would be clearer than Accepting a Person into a Group.
## Accept/Reject Create
When sending a Create to a remote server, there will generally be side effects interpreted by that remote servers software. One common example is that sending Create Note with `inReplyTo` may attach your Note as a reply to another Note. But this is not guaranteed to happen, e.g. if the remote software has a concept of “disabled comments” or “approved comments”. In that case, the remote software may want to signal that the use of `inReplyTo` will not automatically cause side effects to be processed, in the same way Follow must first be Accepted or Rejected. It might not make sense to explicitly *require* Accept/Reject semantics for such a case (as they are for Follow), but they can still be used for hinting the result.
## Accept/Reject Update/Add/Remove/Delete
In cases where an actor is performing actions on an object other than creating it, these actions may require approval from another actor who “owns” or “manages” that object. You may want to Accept an Offer of an Update, but it would be simpler to just Accept the Update. Per the AP spec, the only considerations made are as follows:
7.3 Update:
> The receiving server *MUST* take care to be sure that the `Update` is authorized to modify its `object` . At minimum, this may be done by ensuring that the `Update` and its `object` are of same origin.
7.4 Delete:
> (assuming the `object` is owned by the sending actor / server)
7.8 / 7.9, Add/Remove:
> * the `target` is not owned by the receiving server, and thus they cant update it.
> * the `object` is not allowed to be added/removed to the `target` collection for some other reason, at the receivers discretion.
So we have some guidance toward the “receivers discretion”, but no formal way of signaling that discretion back to the author of the received activity.
# But why not just use HTTP status codes?
HTTP status codes are synchronously returned at the time of the request, even if the request is handled asynchronously. You may send out a `202 Accepted` to indicate that the document was successfully received, but later send out a `Reject` Activity after some manual action was taken by an actor. This manual action may be performed long after the initial delivery to the remote server.
[/quote]
</article>
---
@nightpool:
I think this is a good idea, but i would like to see some implementor support before defining it further.
---
## current thoughts
yeah, actually. let's do it

View file

@ -1,114 +0,0 @@
+++
title = "Stricter specifications for pagination of Collections and OrderedCollections"
date = "2022-11-05"
+++
## Preserved text
<https://socialhub.activitypub.rocks/t/stricter-specifications-for-pagination-of-collections-and-orderedcollections/2633>
@trwnh:
<article>
## Overview
ActivityPub primarily depends on direct delivery of activities, but many implementations expose old post history via the `outbox`. In theory, you could fetch the outbox to discover old posts that werent delivered to you but should still be visible to you (e.g. Public posts that persist in the outbox). However, there is one big problem: pagination.
Specifically, pagination is an issue because you will have to fetch multiple pages, and you dont know exactly when to stop, or how to discover gaps. You may be able to fetch up until you see a post that you already have, but there may be other unseen posts beyond that one. The only way to be sure is to fetch every single page of the outbox, which can be a costly operation.
## Recommendations
Arguably, this situation can be improved by making some specific recommendations:
### Construct chronologically, but present in reverse order
Because an OrderedCollection is mandated to be “reverse chronological” specifically, extra care needs to be taken to allow stable page references. Perhaps pages should be built chronologically and simply presented in reverse order, crucially with the `first` page containing less than than the max page size.
Example: A page size of 10 and a collection of 23 items should be split into pages of 3, 10, 10. These pages would be presented as such:
```
[
[23 22 21]
[20 19 18 17 16 15 14 13 12 11]
[10 9 8 7 6 5 4 3 2 1]
]
```
### Stable page references should also be reversed
Furthermore, in order to maintain stable page references, such that if youve fetched a page before you dont have to fetch it again, page counters should be assigned in reverse order as well.
Taking the example from above, the pages would be identified as 3, 2, 1:
```
[
[23 22 21] // page 3
[20 19 18 17 16 15 14 13 12 11] // page 2
[10 9 8 7 6 5 4 3 2 1] // page 1
]
```
### Deleted items should either be Tombstoned or change the page size
All this work is useless if the pages have to be recalculated or the items get shifted to a different page. To prevent this, either serve a Tombstone in place of deleted items, or otherwise freeze the upper and lower bound of a page while allowing variable page sizes.
For example, lets say we delete post 17. The result might look like this:
```
[
[23 22 21] // page 3 (3 items)
[20 19 18 T 16 15 14 13 12 11] // page 2 (10 items, 1 Tombstone)
[10 9 8 7 6 5 4 3 2 1] // page 1 (10 items)
]
```
Or, it might look like this:
```
[
[23 22 21] // page 3 (3 items)
[20 19 18 16 15 14 12 11] // page 2 (9 items)
[10 9 8 6 5 4 3 2 1] // page 1 (10 items)
]
```
Notice that page 3 remains unchanged, rather than item 21 becoming part of the 2nd page.
### Accessing pages should be done in a consistent way
The final piece of the puzzle is a way to consistently load specific pages. For example, consider a collection at `/collection/id`. You might be able to attach a query parameter `?page=N` to access the Nth page via `/collection/id?page=N`. Or you might have some route such as `/collection/id/page/N`. Whatever the case, there should be a way of getting pages that can be expected to work across all implementations. Or, at the very least, a way that may be inferred easily, but a standard pagination technique would be better.
My thinking is that `/page/N` would be better, because it would allow for static pages as an option more easily,
Also for consistency: Tombstone is preferable to exclusion, because it allows dynamic page sizing on-the-fly in dynamic servers that use query parameters.
</article>
---
@silverpill:
Perhaps a timestamp filter could solve this problem?
For example, `/collection/id?after=1667766000` can return a paginated subset of the collection, and if the client knows the time of the last sync, it can retrieve missing objects with fewer requests.
This way implementations can continue to use their preferred pagination mechanism.
---
@trwnh:
Having a timestamp filter might work for dynamic server implementations but not for static server implementations. I took care in making sure, while writing the above post, that the recommendations would be applicable to both static and dynamic servers.
---
@trwnh:
in writing this up i seem to have overlooked the `startIndex` property of OrderedCollectionPage, which is basically just a positive offset for how far into the OrderedCollection you are with the first item. unfortunately this is less useful due to reverse chronology being mandated by ActivityPub; it would be far more useful in a forward-chronological collection. it also doesnt apply to regular CollectionPage sadly.
there is still the option of having a regular Collection contain OrderedCollectionPage, though, or otherwise simply disregarding the “MUST be reverse chronological” bit and committing a spec violation.
---
## current thoughts
idk. really wish the OrderedCollection wasn't mandated to be reverse-chron and it was instead forward-chron at least. this carries over to OrderedCollectionPage too...

View file

@ -1,53 +0,0 @@
https://mastodon.social/@trwnh/110361352685140966
so we might reasonably recreate bluesky's "placeholder" nameserver by having a well-known endpoint for resolving identifiers to as2 documents (with possibly a layer of indirection, in which case webfinger would probably work)
this basically replaces DNS for resolution. you'd just have to figure out how to assign ids in a distributed way without conflicts
for that matter: why isn't xrpc a .well-known endpoint? it absolutely should have been
i'm thinking if we wanted to reify webfinger and identity providers, we could just have identifiers assigned under the authority of webfinger subjects. where is the data? idk, check the current links via webfinger.
problem with that is, your IdP better be long-lived... in fact, it makes sense for a persistent IdP to last basically "forever" and just point to your current data stores. and then i guess you'd want IdP federation to ease migration between IdPs? or otherwise conflict-free id minting
tldr the open problems:
- how to assign ids without conflict? (preferably without dns or key based authority, so you can change those later)
- how to resolve those ids across multiple servers without tampering? (see also: dns poisoning and authoritative nameservers)
there's a lot of prior art in DNS and you'd be moving it all down to the HTTP level over webfinger and oidc and related stuff
basically instead of resolving names -> authoritative DNS nameservers -> IP addresses, you have to resolve uh... names -> authoritative Webfinger servers -> http(s) links
so i guess we need an equivalent to DNSSEC but for WebFinger
---
erincandescent writes:
<blockquote>
hash of public key; hash of first version of document (in chain), like they did. many decent options!
the did:plc approach allows you to rotate keys because your current identity document will eventually chain back to the original one which hashes to the value of your account ID
the bit the central server is doing in this system is - basically - ordering things & letting you use your recovery key to oblitterate any updates from your primary key from the last 72 hours (for if your primary key was compromised)
the key difficulty without any central server is timestamping things to ensure & validate ordering
</blockquote>
cobaltvelvet writes:
<blockquote>
i think i just like the idea of a minimal relay as this resolver, that would keep the least state possible inside it, so it can be swapped in and out easily, maybe even transparently so by your instance. but it would always need A Way to validate the 'latest location', either the keypairs that arent very securely held atm but instances already have, or a third secret thing (a dedicated channel, like making the resolver email you to confirm a migration, or another layer of signatures)
if i had to point to an inspiration, i think bittorrent trackers are great, easily replceable by a DHT, but also easy to just use two for redundency . ofc they don't really have to bother with any validation, but your instance can
</blockquote>
---
i'd just need to think through how to get consistency between different webfinger services, so that they agree on the response and serve you up the same JRD from some decentralized/distributed data store

View file

@ -1,4 +0,0 @@
+++
title = "threads"
summary = "originally posted as threads on social media"
+++

View file

@ -1,7 +0,0 @@
+++
layout = "search"
url = "search"
name = "Search results"
[_build]
list = "never"
+++

View file

@ -1,6 +0,0 @@
+++
title = "some of my threads out of context"
summary = "mostly a compilation of times i replied to myself on social media or in chat"
+++
[[rejected outtakes]](misc.md)

View file

@ -1,27 +0,0 @@
+++
title = "an analogy between activitypub and programming languages"
summary = "you don't say \"compatible with c++\" you say \"written in/with c++\". similarly, we might say that fedi is \"written with activitypub\" or \"implemented using activitypub\""
date = "2023-03-28T09:13:00-06:00"
source = "https://mastodon.social/@trwnh/110101321320240671"
+++
people think activitypub is a network protocol when it's really more like a programming language. you don't say "compatible with c++" you say "written in/with c++". similarly, we might say that fedi is "written with activitypub" or "implemented using activitypub", not "compatible with activitypub" or anything suggesting a network.
analogously:
ap = inter-process communication (e.g. d-bus)
as2 = data interchange format (e.g. protobuf)
json-ld = media type
rdf = facts and logic
if we're being honest the majority of "fedi" is part of the "network" that is "compatible with mastodon". mastodon *is* the network protocol. we just express it in terms of "mastodon-flavoured activitypub"
put another way, there is no such thing as "fedi", but rather, multiple different and distinct "fedis" that only partially overlap.
it occurs to me that activitypub may be best thought of as a cms with an optional distributed storage backend
similarly: no one in fedi is doing real activitypub. it is probably more appropriate to say we are doing AS2 + Linked Data Notifications
side effects are more accurately defined by the mastodon documentation than by the activitypub spec
we're kind of in the situation where someone has invented the C Programming Language, maybe D-Bus, but we still don't have a FreeDesktop.org or XDG equivalent.

View file

@ -1,22 +0,0 @@
+++
title = "art is not a commodity"
summary = "the real answer to making sure artists get paid is to actually pay them for the work they do, not to commodify what they produce"
date = "2023-01-02T21:00:00-06:00"
source = "https://mastodon.social/@trwnh/109623038871646811"
+++
well opposing copyright isn't thievery, for a start. theft is the act of taking something without returning it, removing the original. digital information can be infinitely and perfectly copied.
copyright enforcement is instead about giving certain parties exclusive monopolies on the spread of information. this gatekeeps culture and stifles free expression. in return for what? trying to sell access?
note also this monopoly is only enforced via the threat of state violence.
tangentially, there's also the fundamental inversion of value that occurs when artists try to fund themselves by selling art as if it were a commodity. the value in art is not necessarily in the final product alone but moreso in the creation of it.
copyright asks us to perform creative labour for free, and hope to recoup our losses via the market (in which you must differentiate yourself from millions of functionally equivalent forms of art and entertainment -- good luck!)
when you recognize this inversion of value, you recognize that the real answer to making sure artists get paid is to actually pay them for the work they do, not to commodify what they produce. things like commissions, patronage, public works funds, etc. all get to the root and heart of the issue.
copyright is in effect more akin to theft -- theft from the public, from the commons, from culture. it benefits no one except those who "own" a lot of art -- the disneys of the world.
in short: if you want artists to get paid, copyright is a really poor and ineffective way to do that. it just leads to big monopolies on art, at the expense of everyone else.

View file

@ -1,16 +0,0 @@
+++
title = "blocked"
summary = "it's the finality of such a situation that bothers me the most; the knowledge that no amount of apology will matter, nor will any attempt at reconciliation even be seen"
date = "2018-02-27T21:23:00-00:00"
source = "https://mastodon.social/@trwnh/99599426061591957"
+++
i wish i wasn't autistic because i'm really bad at reading social situations and here i am thinking i'm making light banter when instead i'm one post away from being blocked, before i can even apologize for misreading intent. end result is i just feel extremely shitty and like i'm terrible at being a human. i don't know how to vent my frustrations without it seeming personal, apparently
also it's the finality of such a situation that bothers me the most; the knowledge that no amount of apology will matter, nor will any attempt at reconciliation even be seen. i've never been able to properly handle irreversibility. even though a block is technically reversible, it isn't practically. one mistake and it's over. and i *will* make those mistakes, because not only am i not perfect, but i'm hopelessly broken beyond repair, no matter how hard i try to learn how to socialize.
and yet... i've not really been blocked much in my life (bar some democrats on birdsite who refuse to listen to left-poc) so i'm not totally insufferable or an absolute troll or a toxic personality. but of the more "misunderstood" blocks, one particular one comes to mind, because it was the only case where that person actually unblocked me years later. but that was much worse and what i said then was extremely insensitive. i wish i knew why they unblocked me. i'm too afraid to ask.
sometimes i wonder why i care what total strangers think of me, and i don't know if it's better or worse to not care. it's not "shame" or "approval-seeking"; it's more of an intense urge to not be hated or begrudged.
i guess that's a part of life, but it's not a part i'm comfortable with and i don't know if i ever will be.

View file

@ -1,10 +0,0 @@
+++
title = "boktai deserved better"
summary = "where do you go to find quirky existentialist video games based on norse mythology and spaghetti westerns in the year of your lord 2018, you ask? you don't ;_;"
date = "2018-04-15T19:54:00-00:00"
source = "https://mastodon.social/@trwnh/99865201213509948"
+++
i'm so emo that boktai 3 never launched outside of japan and also that boktai as a series is super dead and konami will never continue it and kojima probably can't/won't either due to copyright and the fact that it's been over 10 years since boktai ds and also kojima doesn't work for konami anymore UGH
where do you go to find quirky existentialist video games based on norse mythology and spaghetti westerns in the year of your lord 2018, you ask? you don't ;_; #boktai

View file

@ -1,23 +0,0 @@
+++
title = "don't worry about camera gear"
summary = "I wouldn't recommend going for ILCs unless you have a dedicated use case [...] Once you've picked a niche, then the gear choices will result naturally."
date = "2018-04-09T22:28:00-00:00"
source = "https://mastodon.social/@trwnh/99831836537266861"
+++
> On a less cranky note, I really want to get myself a nice digital camera. Any photographers out there with strong opinions on the matter?
* What type of photos do you want to take?
* What's your budget?
* Do you want to deal with carrying multiple lenses / heavy bodies / etc?
* What's the destination / use case for your photos?
> all kinds. Budget isn't too much of an issue I can just save up longer. Multiple lenses are fun and I'll probably get a couple solid bags so not too put out about weight. Destination unknown.
Well, I'm just going to say that if you don't have any idea at all, then that also means you can't really get a good answer. I wouldn't recommend going for ILCs unless you have a dedicated use case -- a compact or even a smartphone will do fine in most general cases. (I picked up a Sony RX100 M2 used for a few hundred bucks for my general shots.)
Once you've picked a niche, then the gear choices will result naturally. As a general rule, spend more on glass than on bodies. Probably pick two telephoto lenses, one below ~70mm (down to 35mm or 24mm) and one above (up to about 200mm). Get a body with a sensor that's at least APS-C, and at least 12MP. A Sony a6000 or a6300 will be most versatile in the $750-1000 range (body only). You'll be spending an additional $400-$1000 for each quality lens you also pick up.
> fair enough! I'm just doing my research right now so I appreciate the honesty.
you're welcome :) it's really important to know what you're getting yourself into. a lot of people buy in way too early and stick with a kit lens because they didn't budget for lens -- and at that point, you might as well get a 1" compact, maybe even a Micro Four-Thirds like the Fujifilm X100 series if you can afford it.

View file

@ -1,27 +0,0 @@
+++
title = "stop using crossposters"
summary = "crossposts really don't engender organic engagement. They feel robotic and distant, largely because they're just that"
date = "2017-11-26T02:20:00-06:00"
source = [
"https://mastodon.social/@trwnh/99069749589979672",
"https://mastodon.social/@trwnh/99093644445157168",
]
+++
Stop using crossposters.
Not to, like, force y'all to do something, but crossposters are self-defeating.
It's far better to commit to a network rather than just make a carbon copy of yourself, because if you're posting exactly the same things, then what even is the point of having two networks?
That's just unnecessarily redundant.
I've been through this kinda rigmarole before when I tried using diaspora*, and the end result was that I completely abandoned it because I wasn't getting any meaningful interactions out of it compared to Twitter. Which was a shame, because I really liked diaspora*.
The problem, of course, is that you will inevitably gravitate to whichever platform nets you more interaction. And crossposts really don't engender organic engagement. They feel robotic and distant, largely because they're just that
## followup
ugh i really hope crossposters don't slowly choke mastodon like they did to diaspora*
if you're just crossposting everything you tweet on birdsite then what even is the point of making a mastodon account? that's glorified spam at worst, and a recipe for abandonment.

View file

@ -1,18 +0,0 @@
+++
title = "nothing is truly forever"
summary = "At some point, the final copy of any given data will be deleted. Or it will lose relevance. Or it will slip into obscurity."
date = "2017-12-20T04:53:00-06:00"
source = "https://mastodon.social/@trwnh/99206247716252048"
+++
I just deactivated some really old accounts I had on birdsite, ones I'd stopped using years ago, but had left up as a sort of archive... The last relics of a bygone era, of a personality long dead... A mark of my former selves.
Makes me think about the fact that nothing is truly forever, not even the internet and the data we think will last forever. At some point, the final copy of any given data will be deleted. Or it will lose relevance. Or it will slip into obscurity.
Of course, it was already not as I had left it. Accounts I had once conversed with, deleted. Maybe some of those people met the same fate as their accounts. Who knows? A lot changes in three years.
I can't back up the DMs that have been deleted, and the only copy of the replies are in the notification emails sent out to an inbox of a Gmail I'd long forgotten I had.
Kind of a heavy feeling.
The pictures will be gone in 30 days, but I can't help but think of the pictures lost forever from Twitpic or Yfrog or all of those other image hosts we all used before image hosting became a standard part of any web app.

View file

@ -1,77 +0,0 @@
+++
title = "defining quote posts"
summary = "to me, the way i see quote posts is essentially as a \"loud reply\" or as a \"breakout thread\""
date = "2023-05-01T12:07:00-06:00"
source = "https://mastodon.social/@trwnh/110294523321644375"
+++
fwiw my take about quotes is that either
- they're just links with special tag markup that lets them be identifiable as activitypub objects
- they're supported more at the protocol level a la Announce with content, or possibly a "quotes" collection a la "likes" and "shares"
- they represent a new context (assuming mastodon ever adopts a first-class concept of conversations or threads, instead of relying solely on inReplyTo chains for logical grouping)
my further idea was to decide if it notifies based on whether the type is Link or Mention (a la webmentions)
in an alternate timeline we could have had generic rendering of activities based on name/summary/content and maybe actor/type/object
such a generic renderer would be something like...
> `<alice> <Announce> <some-post>` \
> `<Announce.summary>` \
> `<Announce.content>`
filling in the last two lines for summary and content
> alice boosted a post: "The problem with this is..." \
> "I disagree with this analysis."
also this just highlights what i think a "quote post" should really be: more like a "loud reply". in a more ideal world it would use `inReplyTo` + a new `context`.
yes, a regular "reply" is in effect a "quote" the second you embed the post you are replying to. look at the IndieWeb for example. what is the functional difference?
the problem of course is that mastodon doesnt show replies in timelines, and doesn't have a proper concept of contexts aside from reply-chains. but at least half of that is a matter of policy, so it could be changed.
imagine a property `toot:quoteReply` that is a boolean. if true, it embeds the `inReplyTo`post above your post. it also sends such posts to follower timelines.
fundamentally there's the intent and the interpretation, intent can be seen as how you author the activity, interpretation is the processing rules applied by the receiver. if this were a proper spec, the two would be the same and there'd be no ambiguity, but... well, y'know.
there's also the rest of the ecosystem of course, there's the "tag a substring of your content" as Foundkey does, in the past there was even "attach the post you're quoting" which Hubzilla and/or Zap did, there was also the proposal to "add content on Announce" which gargron and lain favored but never implemented. and then there's the two new ones -- "just make it a special type of reply" as i propose, and "just make it a new Activity" as you expressed just now
we should focus on generic data modeling, describing resources and how they are related. mastodon doesn't do a good job of that, sure... and that's how we end up with leaky abstractions. it's a good idea to leave as much of the app-specific stuff out of the data model as we can. otherwise we just end up with The Mastodon Protocol which is built to interop with the mastodon app specifically
like we have tag, attachment, inReplyTo, Announce...
- tag = describing entities within the natural language properties (often microsyntaxes)
- attachment = sidecar entities (not necessarily media)
- inReplyTo = referencing a prior object (or objects) for which your current object can be considered a response
- Announce = add to `shares` collection if present (this is the weakest one, to be fair)
> I do agree with you that quotes are basically a form of loud reply
semantics aside, i think functionally the difference is really the fallback
- an Announce with content is basically "reshare with additional content" semantically, but it falls back to being a regular boost, and to control quotes you'd need to similarly be able to control boosts
- a Note with inReplyTo + quoteReply=true would fall back to a regular reply. this means that when reply controls get added, you get quote control "for free"
the other functional difference between those two is whether the "quote" gets added to `shares` or to `replies`
---
also tangentially i've thought about what people think a quote post is vs what it actually is, the dimensions, its primary function, etc
to me, the way i see quote posts is essentially as a "loud reply" or as a "breakout thread".
the former use-case could be handled by reply+boost. this is actually very easy in the API: just make a button that fires off 2 API calls.
the latter could be handled by copypasting a link into a new post. this is much harder to detect and display. optionally mention
really the problem with twitter "quote tweets" is that they conflated the two use-cases
you were *always* notified, regardless of whether someone was talking "about" you or "to" you (no consideration for replies)
plus the fact it was a new thread was stripping context, you could no longer see all the posts in one thread ("breaking out" isn't always wanted)

View file

@ -1,12 +0,0 @@
+++
title = "drowning in awareness"
summary = "if you don't have a material call to action, you have nothing. [...] \"awareness\" on its own is not enough; it can even be an actively bad thing if you end up paralyzed by inaction and despair [...] like, ok, i'm aware. now what?"
date = "2023-01-20T00:16:00-0600"
source = "https://mastodon.social/@trwnh/109720070743856424"
+++
i am reminded of an essay i read once called "avoid news" which might sound extreme to some people but it actually has a simple premise: most of what you encounter in media suffers from context collapse and/or is not actionable. therefore it can never truly be relevant. what is far more valuable is filtering through the news for anything actionable worth educating people about, and presenting it in a more relevant and digestible way. not everyone should have to do this.
i'd really go further and say that these days, if you don't have a material call to action, you have nothing. what do you expect to accomplish by sharing what you're sharing? "awareness" on its own is not enough; it can even be an actively bad thing if you end up paralyzed by inaction and despairing over it. far better to actively educate people that need educating, instead of simply "raising awareness".
like, ok, i'm aware. now what?

View file

@ -1,52 +0,0 @@
+++
title = "fedi as a reformist option"
summary = "[...] it's holding us back from better things. it's like the \"reform\" option when we need radical change. and it keeps getting \"worse\" [...] ad-free and chronological ain't enough."
date = "2023-04-30T20:27:00-0600"
source = "https://mastodon.social/@trwnh/110290827199480228"
+++
nobody asked but i think fedi as a whole is actually kinda "bad" in the sense that it's holding us back from better things. it's like the "reform" option when we need radical change. and it keeps getting "worse". somewhere along the way we seem to have dropped the "blogging" from "microblogging", and what remains is incredibly muddled. contextual and ergonomic failure. we're copying "social media" and inheriting its flaws. ad-free and chronological ain't enough.
see, we're all mostly here to just hang out and spill a stream of consciousness, right? but there are problems inherent to the structure. context collapse (or no context at all), misuse of the medium (doing a "thread" instead of writing an article), and so on. everything just goes into the square hole.
i posit we can do better. but it's gonna require going back to basics and building out communication paradigms from the ground up. with thought given to effective communication, not profitability
but. (and this is where i am afraid of shit-stirring)
no one wants to do that
the people working on fedi past and present seem mostly invested in mass adoption, and the way to do that is to keep building on broken foundations. and it feels kinda cultish at this point, like the goal is "success" defined by "growth", and it's a branding game, it's lip service.
"mastodon", even "activitypub" itself, is seemingly about mindshare more than anything tangible.
i don't particularly want to call out specific people for this, because it's not just a few people, it seems to be a cultural issue. the framing is that we can replace those social media platforms instead of tearing them down as we ought to. mastodon wants to become the next twitter. pixelfed wants to become the next instagram. and so on.
on some level i think the people are just looking for validation of their work. but this leads to a kind of conservative outlook toward actually improving...
yes, this thread could have been a blog post. that's part of the problem
---
i mean we stop doing "social media" as twitter and facebook made, and go (go back?) to clearly publishing and clearly discussing, as two separate things. remember blogs and forums? on blogs, you publish. on forums, you discuss. on "social media", this distinction gets collapsed. a "post" and its "replies" are all in this one big global context by default. who is the audience? what is the purpose? these questions don't have clear answers on "social media". corpos profit from this ambiguity.
in "social media", the audience is "whoever we put this in front of" and the purpose is "an endless feed of posts for you to graze from". contrast this with the intentionality of cultivating your own audience, and of communicating with them. fedi gets us halfway. we move away from the profit-oriented mindset, but we keep the trappings and mechanisms that were created in service of them.
tldr we're on the Web and we should remember that. death to "platforms". empower actual people to set up their own spaces and domains.
---
there is an argument to be made that the right thing would not have been made clear if we hadn't kept making mistakes like chasing the big corporate players. i'd be open to this argument 10 or 15 years ago around the time of identica or diaspora, but since then it's just become more apparent that this whole space will never be anything more than a transitional holding-place for people who leave the walled gardens for... fenced gardens, i gues? and the better thing hasn't been built yet.
---
here on the World Wide Web you have two threats: capitalist enclosure, and toxic community. and honestly the latter is probably less bad, because at least you have the self-determination to make your own community with your valued friends if the current one gets too bad. with the former, you have no escape.
in short, "social media" is a failed model invented by capitalists. what we need is a "social web". focus on people, not "content".
want to entertain yourself? that's fine, there can be media hubs and curators for that. but your communications and your media consumption should be separate.
---
honestly as much as meta gets tiring, at least these are conversations that can actually be had with a nonzero chance at actually shaping things.
you think twitter or instagram gives a shit what its users want or think? nope, it's all about driving ad revenue

View file

@ -1,219 +0,0 @@
+++
title = "fedi vs web"
summary = "this is the fundamental divide between #fediverse thinking and #Web thinking, where #ActivityPub straddles the line between both."
date = "2024-09-24T07:06:00-06:00"
source = "https://mastodon.social/@trwnh/113192442723669483"
+++
idk where to really put this (might turn into a blog post later or something). it's what you might call a "hot take", certainly a heterodox one to some parts of the broader #fediverse community. this is in response to recent discussion on "what do you want to see from AP/AS2 specs" (in context of wg rechartering) mostly devolving into people complaining about JSON-LD and extensibility, some even about namespacing in general (there was a suggestion to use UUID vocab terms. i'm not joking)
the main contention is a disconnect between #ActivityPub as a spec and #fediverse as a protocol/network. a lot of problems cited were with the fediverse as implemented, wishful thinking about what could be changed in spec, many backwards-incompatible, mostly in service of making fediverse impl less painful.
there is a recurring refrain about implementers deciding they don't care to implement AP as specified, and that this indicates a problem with the spec, not a problem with implementers.
i think this disconnect between #ActivityPub and #fediverse honestly goes a lot deeper than people might realize. and that is because the problem AP tries to solve is actually completely different from what fedi is trying to do.
the concept of a nebulous but mostly singular "network" or "protocol" (made up of partially overlapping parts) is core to what i'll call "fedi mindset". the assumption is that you can join the fedi "network" by implementing the fedi "protocol". and that AP is this.
but this assumption starts to break down when you look a little closer.
first, consider #ActivityPub C2S. why is there close to zero usage of this in #fediverse software? simple: it doesn't solve any needs for building a "network" "protocol".
now consider S2S. why are there zero compliant impls in fedi? because AP as specified doesn't address the needs of fedi. what does fedi need? well, i find it telling that the "real" reason AP was adopted was... to implement followers-only posts.
which is to say: the primary reason that #ActivityPub is used (to the extent you can say it is being used at all) in the #fediverse is mostly historical.
fedi grew out of a long line of open protocols, and before AP was adopted, it was at the point where people primarily used "activity streams" as their vocabulary and data model, stuffed into atom feeds. atom feeds don't do private posts unless you make an entirely new access-controlled feed, possibly with a token of some sort. hence, AS2.
when #ActivityPub was being standardized alongside AS2 it basically had two compelling reasons for what would become the #fediverse to adopt it:
- it was built on AS2, which was an evolution of AS1, which was already being used. so it wasn't hard to make the jump.
- it made followers-only posts possible, because while atom feeds *could* do this, it was wildly inconvenient to actually do it that way. posting something private to an inbox is a lot simpler, no juggling access control tokens.
but beyond that, what does #ActivityPub actually do for #fediverse as a "network" "protocol"? basically nothing. you have a basic mechanism for delivering activities directly to subscribers, but no specified shape or structure for that payload. and you still need a lot of other specs to end up with something that talks to the "network". even with AS2 vocab, you need more vocab extensions to express things you want to.
simply put, AP is not enough for a "protocol" to build a "network".
but before you build a "protocol" for a "network", consider: what even is a "network", in this context? and, here's the hot take: do you even *want* that kind of "network"? do you want a separate reified #fediverse network?
because the answer that #ActivityPub gives is actually a different one. There is no "AP network", because AP as a protocol is not enough to build a concrete network. it is intended to provide, and exists in context of, the larger #Web.
this is the fundamental divide between #fediverse thinking and #Web thinking, where #ActivityPub straddles the line between both.
i've seen it said that the "open-world assumption" at the foundation of the Web is actually an undesirable thing for a "social networking protocol", and as a consequence, specs built on that open-world assumption are "completely unsuitable" for that "protocol".
but do we need a "social networking protocol"? do we even need "social networks" in the first place?
to build the #fediverse as its own "social networking protocol" then seemingly requires that we instead go with the closed-world assumption, contrary to the #Web
it requires ahead-of-time communication and coordination, where implementers need to be willing and available to talk to any other implementer, and this load grows with every new implementer.
it requires you to be aware of other extensions, present and future, because your extension might conflict with someone else's extension.
the way extensibility works in a closed-world #fediverse is that "every implementer talks to every other implementer". or maybe there is a central registry of extensions that everyone submits to their authority, as stewards of the "protocol" that is used to build the "network". this trades out the n:n relation between implementers and other implementers, for an n:1 relation between implementers and the central registry.
the way extensibility works in an open-world #Web is you just do it.
the challenge in closed-world systems is how to scale communication and coordination as the number of implementers grows. without a central authority, it almost inevitably leads to power coalescing in the hands of the few most popular or largest implementations, who become the "de facto" standard and get to mostly do what they want, and everyone else mostly has to follow if they want to be compatible.
sound familiar? it should, because this is the model that the #fediverse follows today.
indeed, the #fediverse is more closed-world than open-world. you see this in the so-called "rejection" of json-ld among presumably the majority of fedi implementations. because for the most part, AS2 lets you ignore json-ld. it only matters for extensibility, and (specific criticisms of json-ld aside) json-ld also mostly allows you to ignore it.
so why do people still complain about it?
well, there is the concept of "context" in json-ld, which represents shared understanding.
when i say "john knows sally", there are several ambiguities. we can solve ambiguities by disambiguating. one way to disambiguate is to be explicit about what any term or symbol means. one way to be explicit is to use uniform identifiers.
in particular, http/https uris have some convenient properties
- they have authority, so you can qualify an id based on who's assigning it.
- you can use the authority component as a namespace
- you can fetch the uri and it might return something useful
so let's say john is example.com/john and sally is example.com/sally
what do we use for "knows"?
well, there are multiple senses of the word "knows":
1) is aware of the existence of
2) is familiar with
3) is having sexual intercourse with
we mean definition 1. so we might use example.com/vocab/knows/1
now we have the statement:
<example.com/john> <example.com/vocab/knows/1> <example.com/sally>
this is unambiguous, but we can go one step further: we can provide definitions at the uri
say some random person sees the statement above. they don't know who john or sally are, and they don't know what "knows" means in this context.
well, if we do a little work upfront, they actually *can* know what all of these terms mean, **without ever asking us directly**
we put a resource on example.com for each of these terms, and each resource describes the subject of that identifier -- it is a "resource descriptor".
the resource for knows/1 can define itself explicitly with a schema
so at minimum we have the following schema for knows/1
- how to represent it in plain text: "knows"
- how to define it: "is aware of the existence of"
the RDF Schema gives us `label` and `comment`, as defined by the RDF Schema.
- :label "knows"
- :comment "is aware of the existence of"
but we need to know what "label" and "comment" mean as well! not to worry, we qualify those terms with the rdfs namespace:
- rdfs:label "knows"
- rdfs:comment "is aware of the existence of"
now at this point you're probably wondering what this has to do with social networking. and on a practical level, if you're just interested in building a "social networking protocol", this is mostly all extraneous.
the part that implementers have to deal with is the notion of "context" and, more specifically, how json-ld handles it, and even more specifically, what to do when two shorthand terms conflict.
remember, the open-world solution is namespacing. what does closed-world do?
well, let's look at `actor`. in AS2 terms it refers to the entity that performed an activity. but in schema.org terms it refers to someone playing a role in a movie or other performance.
in a closed-world sense, you don't want to be aware of context. you don't want to have to deal with it. but even so, you still have an "implicit context" that you are using, based on how you define each term in your own understanding, what you hardcode into your software.
what json-ld does, or what it allows you to do, is explicitly declare a `@context` that is equivalent to your "implicit context".
this works fine if there is only one declaration that is shared exactly between two parties, but it gets complicated when the "implicit context" differs or isn't an exact match.
this means that there cannot ever be a singular #fediverse network, because the "implicit context" differs between each software project. the only guaranteed overlap is the AS2 one.
but it's not like AS2 didn't think of this. they wrote in this requirement: https://www.w3.org/TR/activitystreams-core/#extensibility
> Activity Streams 2.0 implementations that wish to fully support extensions MUST support Compact URI expansion as defined by the JSON-LD specification.
note, you aren't required to implement all of json-ld. you just need to handle the bit where you can identify the equivalence between a uri and some arbitrary string.
but #fediverse mostly decided this is too hard, and ignore context.
now there's a few thoughts i have here:
#fediverse culturally seems to ignore a lot of other things as well. they ignore http caching for example. they ignore http status codes like 301 Permanent Redirect. these requirements are arguably more important than context, and they *still* get ignored.
in fact, most fedi software is mostly just reimplementing Web browsers, but with what they consider to be the "bare minimum" of compliance. and the web they let you browse is smaller than the Web
are these things part of the "protocol"? how far does the "protocol" extend to cover? because, as we established, #ActivityPub is not enough to build a fully functional #fediverse -- and a lot of extensions and additional specs are things that ought to be included in this "protocol", insofar as this "protocol" is desirable.
the other thought:
if you ignore things, that means there are cases you're not handling, losing out on robustness. ignoring context is to ignore shared understanding.
so what do you actually lose out on when you ignore json-ld context?
you first have to fall back to the "implicit context", where AS2 terms are generally agreed upon, but nothing else is guaranteed.
take something like `discoverable` from mastodon. what does it mean? well, it means whatever is defined in the mastodon codebase and documentation. so we could represent that as `http://joinmastodon.org/ns#discoverable` or shorten that with a prefix. but if we do, then most #fediverse will choke on that.
this is because #fediverse is ignoring context. the implicit context is that `discoverable` means `http://joinmastodon.org/ns#discoverable` but they don't know that. so they can't actually handle the extension in its fullest form.
what AS2 calls out as "full support for extensions" requires being able to identify this equivalence and handle it. again, fedi does... let's call it "partial support".
the "implicit context" is now a hardcoded but unstated requirement of this "protocol".
which is to say: #fediverse software generally expects LD-aware producers to compact against their own "implicit context", but they don't always define that context. it's left undeclared and undefined. or it actually *is* declared, but if you give them their own expanded form then they'll not understand it.
it's like someone saying hey, when i say "knows", i mean "is familiar with"
and then you say "john is familiar with sally"
and they respond WTF? what does "is familiar with" mean?
it's like... you literally just told me "knows" = "is familiar with", but because of your own ignoring of your own context, you can't handle me saying "is familiar with"?
in this way, as long as the #fediverse remains ignorant of context, they will remain fragile and without any sort of robustness in their "protocol".
the alternative they have is to extend the only context they share, which is the AS2 one. but this doesn't solve the problem. it just officially blesses a single term.
if you want to turn "activitystreams" into a "protocol" then sure i guess you can do that
but why? what are the needs we're trying to address here? of what purpose is your "protocol"? social networking? you want a "social networking protocol"?
before you convince people that a "social networking protocol" is necessary, you have to convince people that a "social network" is necessary.
but more importantly, you are contrasting that "social networking protocol" against the "social Web".
it is my personal belief that this whole "closed-world social network" vs "open-world social Web" thing is leading to a big disconnect that makes addressing people's needs harder.
because, to be on the "network", you neglect being on the "Web".
sure, your software might still publish your "posts" as Web resources, but that's it. you're not actually granted control or ability to manage Web resources for yourself.
and that's why #ActivityPub C2S is being neglected, among many other things
i am personally more in favor of a "social Web" than a "social network".
what i want to do is make it easier for anyone to make a website, and to manage that website.
i want those websites to be able to link to each other in well-defined and clearly-understood ways.
i want to make friends and express myself to the fullest, in varying contexts on various websites, without context collapse.
but it feels like #fediverse is more interested in replicating the "social network" paradigm.
---
addendum 1
there's a whole lot of things i could say about "how we get there" but the thread was getting long enough and i want to cut it off here and clean it up into a blog post or something, without drifting too far off the original topic which was to voice my thoughts about the divide itself
addendum 2
there's a separate thought experiment you could do about what it really takes for a "social networking protocol" because honestly you don't even need http. you can do "social networking" over xmpp or email or whatever. or invent your own way to send bytes over tcp/udp/whatever (inb4 xkcd)
seriously tho, newsletters and deltachat and movim and a bunch of other things show that you can do it
---
also i should mention since this is happening kind of simultaneously, this is not about the social web foundation's use of the terms "social web" and "fediverse", although the blog post did go live in the middle of me writing the thread which is a kind of irony i guess. another irony is that even though it's not about that, it could still be kinda about that. if nothing else, it demonstrates that "social web" and "fediverse" are not synonyms.

View file

@ -1,16 +0,0 @@
+++
title = "gun culture in america"
summary = "a reactionary gun culture that perceives any attempt to curb it as an assault on their very identity"
date = "2018-02-18T01:48:00-00:00"
source = "https://mastodon.social/@trwnh/99543874314017409"
+++
[...] although in fact, having a permanent standing army is less legally supported than gun ownership. constitutionally, the longest that congress can raise/fund an army is 2 years...
the larger problem is that the culture around violence in america is absolutely toxic. the war of 1812 eroded the resistance to standing armies, the fugitive slave act of 1850 let the army act as a police force (only on the president's orders post-1878), in 1904 then-sec of war decided to station foreign troops, and in 1947 the dept of defense was formed to integrate the army into the govt. so militias aren't really a modern application of civilian defense, since the govt "took over" it.
the biggest strain was actually when the black panthers decided to arm themselves for community self-defense; white supremacists were scared and started to push for gun control. not really the first gun-scare about black people; one of the arguments used during the dred scott case was that freed slaves couldn't be citizens because then they would legally be allowed to have guns.
anyway, the NRA got taken over in the 1970s and converted from a marksmanship club to a political advocacy group. most of the people involved in this takeover decided to try another approach: take a hardline stance against any gun licensing (to better form white militias), and to strip the black community of its guns by criminalizing them, usually in conjunction with something else...that thing was the introduction of drugs and crack into black communities, which was criminalized once people got addicted to it. furthermore, gun possession drastically increased sentencing if convicted for drug offenses.
the end result of all that is a reactionary gun culture that perceives any attempt to curb it as an assault on their very identity. and when you call them out on this, they deflect to something else like "mental illness" instead. in other words, the whole of american gun culture revolves around a false sense of freedom and safety, directly descended from white supremacy and toxic masculinity. people commit shootings out of a desire for fame or notoriety, or out of some perceived slight or grievance, largely based on ideological assumptions or entitlement.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

View file

@ -1,46 +0,0 @@
+++
title = "collections should be explicitly managed"
summary = "the way to maintain an authoritative collection is [...] not to have everyone send out Create and then expect everyone else to reconstruct the collection for themselves [...] we're just replicating stuff around and then every server assembles it however it *thinks* it should be put together."
date = "2023-03-18T03:51:00-06:00"
source = "https://mastodon.social/@trwnh/110043428576595588"
+++
how it feels to talk about #activitypub and how #fedi implements it
![](create-note.png)
the way to maintain an authoritative collection is to send out Add/Remove, not to have everyone send out Create and then expect everyone else to reconstruct the collection for themselves
i can't say i blame developers for taking the lazy route and doing the bare minimum to be compatible with what already exists, but that is so limiting and i'm not here for it
the bigger problem is that there's so many assumptions being made with zero basis or foundation in anything. we're just replicating stuff around and then every server assembles it however it *thinks* it should be put together.
we're getting to the point that the contradictions and shortcomings are starting to become more apparent...
- things like moderated conversations are impossible if you don't actually have a concept of a "conversation".
- there's so many different ways to do "groups" that it's a running joke at this point, because everyone has a different idea of what a "group" is. it's the "blind men feeling an elephant" problem.
- things like forums and subforums, chat rooms, etc are not easily possible rn.
the "trick" is that there isn't actually any difference between all these different presentations. if you understand "objects" and "collections" you can construct whatever you want: forums, walls, chat rooms, conversations, aggregators, whatever. it's all just objects in collections.
right now, no one understands "collections". we just pass around objects with Create and Announce and leave it up to the consumer to figure out how they should be grouped.
it's not even a new idea. Dennis Schubert raised a similar criticism of "replies" distribution back in 2018: https://overengineer.dev/blog/2018/02/01/activitypub-one-protocol-to-rule-them-all.html#replies-and-interactions
> As per the ActivityStreams spec, all objects [can] have a replies property. So, a more sensible, reliable, and even more AcitivityStreamy way of handling replies would probably be adding the interaction to the replies collection
this is not always optimal of course -- it was proposed 5 years ago. but something similar could be done.
i think the key disagreement, misunderstanding, lack of shared reality, etc, is this:
are we just creating a bunch of "posts"? microblogging or regular blogging, we have our own websites or web profiles, and we only care about what we ourselves are sharing?
or are we trying to collaborate across domains? are we building a Web, a Social Web, a Linked Data Web, a Semantic Web? where our actions have side effects, and there exists some state that we care about?
we lack that clarity of purpose
basically we're not doing one or the other, we're kind of mishmashing them together. when you post on most fedi projects, you are publishing to your profile locally, *but* you are also sending out an activity to notify your followers. and typically, that has side-effects on remote servers. usually that side-effect is "keep a locally cached archive of that post and show it to followers."
i think we should be clearer about the separate use-cases and concerns.

View file

@ -1,63 +0,0 @@
+++
title = "liberalism and the rise of trump"
summary = "there's some broad historical revisionism going on"
date = "2018-01-14T10:35:00-00:00"
source = [
"https://mastodon.social/@trwnh/99347736716090638",
"https://mastodon.social/@trwnh/99355650165263881",
"https://mastodon.social/@trwnh/99359516579847229",
]
+++
> some vox article about trump and liberalism
eh, the article and its analysis has many issues with it
- liberals aren't leftist
- "reforms" were gained by strikes and protests, and it was anarchists and communists that led the struggle, not liberals
- presents japanese internment as a "flaw" and not a great injustice
- "whether the state has a responsibility to ensure a moral and socially desirable distribution of wealth" is irrelevant; welfare capitalism is still capitalism
- you can't "correct" inequities by using the institutions that exist to propagate them; they are working as intended
- "appealing to national pride" is fascist
- anticommunism and mccarthyism WAS a major reactionary force, not a "purity test" by the left
- the "strategy" you quoted has mostly been adopted by modern democrats in reaction to the rise of the "religious right" and other ratfucking attempts
- the left was sabotaged by the fbi/cia
- academia and technocracy aren't hallmarks of the left, either
- "the hard work of liberal democratic politics" is, again, fruitless; the real issue is the complete defanging of the left by COINTELPRO and other initiatives; we need more haymarket squares and more may days, not endlessly begging politicians to not murder us
- in short: yes a revolt is justified because america was founded on a fascist mythos of expansionism and exceptionalism
i guess in conclusion i'd say that the "make america great again" point is really close to being on the money
- the right = "make america great again" by national rebirth (one of the 3 main tenets of fascism)
- the liberals = "america is already great" because we just need a bit of reform (and all horrors are simply aberrations)
- the left = "america was never great" because it was explicitly founded as colonialist/imperialist/white-supremacist
> [...]
no, the extreme right wants a reactionary / openly fascist state that broadly uses violence to liquidate "degenerates" and other "undesirables". the state would be an even stronger force of national supremacy and authority than it already is.
I guess the larger point I'm trying to make is that there's some broad historical revisionism going on, as an attempt to maintain authority. the idea of the nonviolent reformist solution to everything is not how most social revolutions work. this wasn't new to the 1960s; a much longer history of struggle against state violence exists, going back through all of america's colonial history
i.e. as a nation, we've never "reformed" anything. radical abolitionists like john brown and the free soil party did more to end slavery than abraham lincoln ever did. the black panther party's community policing and radical housing and food programs were effective despite the fbi assassinating almost every single one of their leaders. fdr proclaimed to businesses that the new deal would "save capitalism" from the labor insurgency.
the original article kind of glosses over all this in an attempt to paint trump as a new phenomenon and not the natural result of weak reformism utterly failing to uplift anyone. the central thesis is that the left failed because of a lack of bourgeois democracy, not because of pervasive sabotage efforts by the state like cointelpro
> [...]
i can see that. i grew up in alabama, and there's a rich history of communism down here. (Hammer and Hoe is a good book to read about this)
the big switch from dem to GOP was an orchestrated effort to create an "identitarian" base of support. that is, by building up whiteness, by having politicians speak at churches and then funding preachers to spread political messages, etc... political parties hope to become functions of identity
the alt-right is, in fact, the most modern outgrowth of the "identitarian" movement -- their politics are descended directly from the cynical traditionalism employed by the political right for over half a century. the people who grew up with this stuff eventually realized that the GOP was in the same boat as the dems; that military contracts and tax subsidies was their only real aim.
but the real problem was that there was no opposition to this idea -- the fbi destroyed it with cointelpro and by assassinating anyone who became too influential.
in this void, the liberals decided that they would try to minimize racial tensions rather than approach them head-on. the outgrowth of this was the dlc, which argued that the democrats had gone "too far to the left" with the new deal and great society legislation.
if i had to pick a reason why much of the south doesn't like democrats, it would be that they just don't trust them. and their visible options have been restricted to the democrats and republicans, which is barely a choice at all. so some people vote gop because they think it'll trickle down or their taxes will go down. democrats are stuck in this perception that taxes and welfare are the only way forward, which doesn't really net much
but way more people around me are nonpolitical. most people don't vote. they're too busy working at jobs that don't pay enough, and either voting hasn't done anything for them, or they CAN'T vote. the conversation is dominated by republicans who promise lower taxes, and by democrats who insult them. every time alabama comes up in national news it's always derisively, and people notice that stuff.
i very much believe that if were any legitimate opposition to the established order, it would gain supporters in droves... but the state has prevented that from being viable by neutralizing opposition wherever it finds it. the FBI and CIA, as part of the "national security" apparatus, take the view that any subversion is harmful to national security. and i mean, they're kinda right. but that's the whole problem...

View file

@ -1,44 +0,0 @@
+++
title = "mainstreaming mastodon"
summary = "[...] one of the replies was something along the lines of how we should be \"turning Mastodon from a small platform for the fringes and oppressed minorities to a platform that welcomes more mainstream members\" and i legit shuddered while reading that. what a terrifying thought."
date = "2023-02-02T08:40:00-06:00"
source = "private"
+++
the shit i see on the mastodon issue tracker is driving me to just build my own thing because i honestly think whatever good culture mastodon has is quite possibly not long for this world
some people literally just walk in and ask "why is this not like twitter" and demand that it be changed to suit their whims. and i feel like gargron is starting to take their side because gargron just wants More. old users be damned, they're just a vocal minority now.
i mean, i wanna be wrong, but i don't actually think there are any guiding principles behind mastodon except "whatever gargron wants", so it is at the very least a possibility, if not a credible threat
there was a post i saw earlier yesterday about something like, idk, i think biz stone said he was advising gargron, and one of the replies was something along the lines of how we should be "turning Mastodon from a small platform for the fringes and oppressed minorities to a platform that welcomes more mainstream members" and i legit shuddered while reading that. what a terrifying thought.
on some level, sure, it's probably cool for "mainstream" people to have something healthier than corporate social media. but i'm really concerned that they'll ruin it for the rest of us, exactly those "fringe and oppressed minorities" who were here for years. years ago we talked about the sort of "detoxification cycle" that new users would go through, and with every new wave, people acclimate less and less. they import more of their old behaviors uncritically.
ultimately? i think things will probably settle in at a point that's better than others but worse than it could have been.
this exact kind of mentality by newcomers who feel entitled to influence
> you don't owe the top of the pyramid jack squat. The early movers *need* you more than you need them.
if i may be so frank, can i just say: we don't "need" you *at all*. our experiences are rich and valuable and span over half a decade. you're certainly welcome to be around here, and you're welcome to use this software, but you have no right to demand we center you above ourselves.
also the idea that there's some sort of "secret mastodon development cabal" is ridiculous. for better or worse, there's gargron, and i guess in some ways there's claire, and that's about it. everyone else is simply acting independently in their own interests and on their own agendas. the entire reason i worry can be summed up by the fact that gargron has unilateral decision-making power and final say. but, to his credit, he does consider the consequences.
anyway, anything i say is with the disclaimer that i am not a representative of anyone else, but perhaps i should be more overt and assertive about it. when i say something "without 100% certainty" i am not implying a vague recollection of events, i am only indicating that i have no decision power.
tangentially, the real problem with these design decisions in mastodon is that they're inconsistent, precisely because it's all on gargron, and gargron is not strongly in favour of these decisions. it all depends on the feedback and advocacy of community members to convince him to do anything, and then he'll do it however he wants to or ends up doing it.
real consistency? we'd not have numbers in explore/trending either, nor on detailed status views, nor in api. no "1+". it'd probably be some boolean like `has_replies` or something, which translates to an icon indicator to show the thread.
but doing it that way takes commitment. it takes decisiveness. and that's simply not there.
there's a lot of these scattered around the project codebase -- places where gargron wanted to do something one way, a lot of the community had to vocally disagree, and we ended up with a half-measure
there was a similar case around hiding your follower counts, where gargron decided to only allow hiding the contents because he thought follower counts were important signals of account quality. even though the counts can be faked (and they have been!)
once again, though... you have to give the guy credit for not rejecting all this outright. it's the only reason we ended up with anything better than twitter instead of Yet Another Twitter Clone.
to quote someone anonymously:
> i don't like a lot of the decisions mastodon makes but i god damn respect "we have a philosophy and that's why we are doing it that way" in particular if the philosophy is thinking carefully about how decisions affect the minority

View file

@ -1,10 +0,0 @@
+++
title = "making better stuff is unprofitable"
summary = "there will inevitably be a point where it is unprofitable to improve a product any further."
date = "2017-12-22T06:06:00-0600"
source = "https://mastodon.social/@trwnh/99217860566004390"
+++
There will inevitably be a point where it is unprofitable to improve a product any further.
i.e., if Apple's primary goal was to make useful products, it would make choices that result in a better product even if it was slightly more expensive. But their primary goal is profit, as it is for every corporation under capitalism. Making better stuff is unprofitable.

View file

@ -1,12 +0,0 @@
+++
title = "the primary value of mastodon is in its api"
summary = "mastodon isn't really all that special [...] but the second you try to have a mobile app, you immediately recognize the value of the mastodon api [...] it's a shame it isn't a real standard..."
date = "2023-02-06T00:56:00Z"
source = "https://mastodon.social/@trwnh/109815069913581608"
+++
to what extent can it be said that the primary value of mastodon is in its api
mastodon isn't really all that special in the fediverse or as a software, because you have so many options and differing implementations of the activitypub protocol. but the second you try to have a mobile app, you immediately recognize the value of the mastodon api. it's a pseudo-standard at this point because it mostly just works, and pleroma/pixelfed/gotosocial copied it for compatibility with existing clients. it's a shame it isn't a real standard...
or, rather, what would a real standardized microblogging api look like? the nearest thing is probably micropub/microsub which doesn't quite do as much

View file

@ -1,112 +0,0 @@
+++
title = "mastodon as a twitter do-over"
summary = "it would probably behoove us all to consider what mistakes twitter did and how we can avoid them."
date = "2018-01-12T06:20:00-06:00"
source = "https://mastodon.social/@trwnh/99336824877757248"
+++
i wonder if it would be a good idea or a bad idea for mastodon to adopt a way to view popular hashtags
pros: better discovery
cons: going down the same road twitter did
actually it's kind of interesting to think of mastodon as a twitter do-over, because it would probably behoove us all to consider what mistakes twitter did and how we can avoid them.
## hashtags
we're obviously past the age of posting toots via sms, so let's start with hashtags.
* aug 2007: hashtags proposed by users as irc-channel metaphor
* july 2009: twitter starts hyperlinking hashtags
* 2010: twitter starts tracking "trending topics"
but of course the mistakes that twitter did were to attempt to apply an algorithm to trends, meaning words and phrases could trend, and some hashtags could be filtered out or censored.
## external media
i think we're also past the point of external media hosting on twitpic/yfrog/etc, since internal media hosting has become far more widespread.
## replying
so the next thing is to look at replying and boosting statuses. starting with replies, because boosting is a bit more involved in analysis.
* nov 2006: @ reply first used
* may 2007: @replies adopted by twitter
not much to say here. i guess i'd note that it makes a service more conversation than status-oriented, but that's about it? conversation is good
## boosting
now, about boosts...
* apr 2007: "ReTweet" first used
* jan 2008: "RT @user" first used
* nov 2009: twitter adds retweet button
so far so good. no need to clog up feeds with multiples of the same stuff. now here's the problem:
* ~2009: people add comments to manual RTs
* apr 2015: twitter adds quote tweets
the biggest problems with quotes? they break context, and they lead to dogpiling. mastodon hasn't adopted quote toots, and probably never should.
## conversation vs content
just about the last thing to add at this point is twitter's attempt to transition from a conversational platform to a curated content provider, especially since 2015. that's bad.
---
## further discussion
### replying to boosts
> who replying to boosts mentions (on twitter, the reply tags both the person who retweeted and the person who posted, whereas on masto only the person who posted it gets the reply)
>
> the 2nd is more nebulous on how it _should_ work and i go back and forth on it tbh. like there's a lot of confusion on who someone is replying to on twitter
>
> but also i have had someone point out that by automatically tagging in the person who boosted it, it takes some responsibility off of the original poster to reply, which helps when you have posts getting more attention than you can handle
when someone boosts a status, and you reply to that boosted status: are you replying to the original poster, or to the person who boosted it? that's the primary consideration there.
twitter steamrolls everyone into the conversation, mastodon doesn't. i like the current behavior. if it's relevant to the person who boosted, i'll manually tag them in, perhaps as a sort of cc.
### replies in home feeds
> how replies to multiple people show up in your feed (generally this has worked by "if you follow one person who is mentioned in the reply it shows up")
>
> i think the 1st is annoying as fuck personally since i'm missing pieces of conversations, especially on mastodon with private posts (and this in turn can affect social ettiquette as people go "my friends are talking, i'll join in" without acknowledging the extra party privately posting)
maybe break it down to decide who's relevant to each new status?
if it's two people you both follow, then i guess it's not too bad to show you their conversation. but social etiquette might mean you shouldn't butt in, if it doesn't involve you. so there's a potential argument to be made to NOT show other people's conversations.
on the other hand, it's generally more useful to follow your friend's conversations, because there is a high chance you'll be interested in them. this is what both twitter and mastodon do right now, and it's kind of taken for granted.
when multiple people get involved, it gets a bit more tricky.
possibility 1: only show the conversation if you follow everyone mentioned
* still shows you friends, and allows friends to join in
* if an external person joins the convo, the convo will continue in their mentions, but will not reach your timeline? it's debatable whether you would want to see ALL of the posts in the convo, or just the ones with your friends. could lead to abrupt disappearance of convos from your timeline.
twitter's behavior is to show you the conversation if you follow the poster + the *first* mention. the assumption is that the post is targeted primarily at the first person mentioned. this may or may not be actually true, though. for completeness, it's assumed to be true.
the only possible extension is to show ANY post from people you follow that mention ANYONE you also follow, which balloons pretty fast.
i don't really have any thoughts about private accounts, because this isn't really an issue on mastodon. mastodon's idea of a locked account revolves around approving followers, but locked users can still post publicly. it's just impossible to see their followers-only toots without them approving you. so, post-level privacy sidesteps this issue; private accounts can still post publicly. much better than twitter's all-or-nothing approach.
### private posts on mastodon
> where private post issues arise is like:
A posts privately, B replies, C replies to B
>
> I follow B and C so I see C's reply to B in my timeline, despite not being able to see the original context AT ALL because it's private
so does C follow A? I would say that ideally, if the original post is privated to A-followers, then all replies are also privated to A-followers. I'm not entirely sure if this is indeed the case; I assume not, since you're asking about it.
It seems easier to handle this for post-comment systems rather than post-reply systems, though the distinction between the two is rather arbitrary. Privacy in the first is encompassing; the second cascades.
Due to specifics of how post-reply systems are implemented differently than post-comment systems (namely, in treating follow-ups as linked nodes rather than as children), I don't really know how best to propagate privacy attributes. It seems a bit strange, protocol-wise, to say that D's reply to C should be limited to A's followers. Perhaps this meshes back with the "multiple reply" issue above.
So I guess the two-birds-one-stone solution is to only show you toots where you follow EVERYONE mentioned. But this has downsides, again as mentioned above... it stifles expansion of social circles. You'd have to go to someone's profile to see all their toots.
Tweetdeck, before being bought out, used to have an option to show ALL replies from people you followed. Maybe bad for social etiquette to jump in with strangers, but it had a use case.

View file

@ -1,126 +0,0 @@
+++
title = "miscellaneous"
summary = "stuff that didn't get its own post"
draft = true
+++
## https://mastodon.social/@trwnh/2264242 [i disagree with my past self somewhat]
the profile model itself is dead [...] streams are the future and also they make way more sense
## https://mastodon.social/@trwnh/99346993416792417
not trying to sell ya, just fawning over how much i'm in love with xfce lol. it's just wild how microsoft decided everything would be 96dpi forever
## https://mastodon.social/@trwnh/99557570047763492
I wish there was a way to search up the title of old children's novels published before 2000 by plot summary or keywords... there are two different books that are on the tip of my mind, but whose titles I've long since forgotten. I doubt they've been digitized or republished, so they may be lost to time forever.
## https://mastodon.social/@trwnh/99793615497112400
hot take: mastodon should be more like email than it already is
## https://mastodon.social/@trwnh/99899695933102322
idk, the thing about e.g. python or ruby is that they focus a lot more on having clean syntax and being easy to understand.
## https://mastodon.social/@trwnh/99937186098244775
> I was under the impression that including "cisgender" in straight was controversial?
I mean... the "controversy" is in how being transgender intersects with heterosexual / heteroromantic. some might argue that "trans people aren't straight" is transphobic because it denies their association with a binary gender. others disagree because being trans is inherently queer and deviates from the "norm" of society.
## https://mastodon.social/@trwnh/99992566429566674
In a way, it's like saying paper should never have been invented, and we should all have continued chiseling stone. That doesn't mean we should forget how to chisel stone; it just means that we shouldn't chisel new stones for *everything* -- paper will do fine in most cases.
## https://mastodon.social/@trwnh/99997039233920487
Hmm... I wouldn't say it's "security by obscurity" in a pejorative way -- there is a very real and very bad habit on Twitter of people "namesearching", or tracking specific terms so that they can jump into conversations.
## https://mastodon.social/@trwnh/100009272925737468
until maybe as late as 2016, twitter thought of itself as a "real time update news network and discussion platform" but now it thinks of itself as "consuming interests and topics"
## https://mastodon.social/@trwnh/100053713095158984
"anarchists are real enemies of marxism" is a self-fulfilling prophecy, fuck. treat someone as an enemy and they become your enemy.
[...]
concept: parody of that old relacore ad ("stress induces cortisol. relacore reduces cortisol.") but replace stress with twitter and relacore with mastodon
## https://mastodon.social/@trwnh/100063071127017012
people aren't always paid to disrupt, they may be doing it for personal reasons, ideology, etc. call them "trolls", it's accurate re: the practice of trolling for attention.
## https://mastodon.social/@trwnh/100072299035734958
it's a false assumption that crypto is trustless
## https://mastodon.social/@trwnh/100085567120616255
hard to say; what makes for a good discourse platform?
## https://mastodon.social/@trwnh/100087757497941062
there's a sort of fetishism around commodity goods from america throughout much of the middle east
## https://mastodon.social/@trwnh/100097247421492501
how to pick a mastodon server
https://mastodon.social/@trwnh/100097303707813337
servers shut down
## https://mastodon.social/@trwnh/100103662264850023
on some level i understand the ignorant desire for change, simply for change's sake [...]
## https://mastodon.social/@trwnh/100105279502219193
by "contradictions" of liberalism I mean things like
- "job creation" when we have the technology to reduce work
- "violence is bad" in the face of endless wars
- "competition is good" when the natural end result is a monopoly
https://mastodon.social/@trwnh/100105523140235220
in general, the liberal/neoliberal split isn't very useful
https://mastodon.social/@trwnh/100109839779411064
"conservative" and "liberal" is a meaningless dichotomy
## https://mastodon.social/@trwnh/100128470703676020
all my friends are cute
## https://mastodon.social/@trwnh/100131913998697452
yeah, idk how i would have gotten off twitter if they hadn't made it extremely easy for me by breaking 3rd-party apps, shadowbanning me being blocked by too many TERFs, and refusing to ban fascists
## https://mastodon.social/@trwnh/100141167073699878
the chronological stuff was like 20% why i left twitter but the other 80% was the "reactionary sensationalist culture" as you put it
## https://mastodon.social/@trwnh/100164745640244312
i don't think it can ever be said that the state is acting in "self-defense". self-preservation? maybe. but self-defense is the domain of the oppressed. at the very least, merely surviving is a radical act. the long-term solution is to defang power and build alternative power.
## some holodomor stuff in 4 subchains
https://mastodon.social/@trwnh/100206391621628088
https://mastodon.social/@trwnh/100206500940583286
https://mastodon.social/@trwnh/100206609835191626
https://mastodon.social/@trwnh/100206657397747867
## https://mastodon.social/@trwnh/100234428636106449
To use Twitter as the perennial example of bad design: say you want to indicate that something is bad. You could previously .@ to make it show up to your followers. Now, you quote tweet it, which exclusively shows it to your followers and not the participants of the old thread. But the third and harder option is to not do a public callout at all.
## actor types idk not rly important
https://mastodon.social/@trwnh/100240521462320522
https://mastodon.social/@trwnh/100240555863529471

View file

@ -1,56 +0,0 @@
+++
title = "re: starting to investigate other protocols"
summary = "The main thing I'm trying to formulate right now is a generic understanding of what is a protocol and what makes up a protocol [...] The \"implicit protocol of the fediverse\" is not sufficiently described by \"the ActivityPub specification\""
date = "2024-09-29T19:53:00-06:00"
source = "https://socialhub.activitypub.rocks/t/socialwebfoundation-what-do-people-think/4564/13"
inReplyTo = "https://socialhub.activitypub.rocks/t/socialwebfoundation-what-do-people-think/4564/12"
+++
<blockquote cite="https://socialhub.activitypub.rocks/t/socialwebfoundation-what-do-people-think/4564/12">
<p>And from an activist perspective, Id be very loud about starting to investigate other protocols and why youre doing it.</p>
</blockquote>
If anything, the recent developments have definitely gotten me to more seriously revisit some ideas I had a few years back about a multi-protocol approach[^multi-proto-approach] and how best to synthesize several different protocols that have similar or different approaches to certain things. The main thing I'm trying to formulate right now is a generic understanding of what is a protocol and what makes up a protocol. To give an example limited to just 3 "protocols" that mostly work off of from/to semantics, we can look at SMTP, XMPP, and ActivityPub.
- Data format: SMTP is more or less plaintext, XMPP is XML, and ActivityPub is JSON.
- Message payload format: SMTP uses RFC 822 messages, XMPP uses stanzas, and ActivityPub uses AS2 Activities.
- Message semantics: SMTP messages have headers and body content. XMPP stanzas can be message, presence, or information query. ActivityPub activities... are activities. That's it.
So before you even get to the part where you can do anything interesting, you find that ActivityPub is really not much of a protocol. All three of these protocols let you "send and receive", but there's more to communication than just sending and receiving. So, given that, what does ActivityPub *actually* provide?
The best I can describe it right now, ActivityPub provides the following "protocols" layered on top of LDN:
- A protocol for "following". You send a `Follow`, it can trigger an `Accept` or `Reject`, you can `Undo` it later, and there are side effects of being added to `followers`/`following`.
- A protocol for "liking". You send a `Like`, there are side effects of being added to the `likes` collection.
- A protocol for "(re)sharing". You send an `Announce`, there are side effects of being added to the `shares` collection.
Everything else is far too undefined or under-defined to constitute much of a "protocol". Things like Create have "surprisingly few side effects" according to the ActivityPub TR, because they were intended for the C2S API to be able to do basic CRUD and Collection management. But in the fediverse, Create actually has a lot more to it! So if we were to define a basic "social media post" application protocol for the lowest common denominator of most of the current fediverse, then we need to define more than what the ActivityPub spec concerns itself with, just to get to "hello world" levels of functionality. We need to explicitly define the following additional things to get from "AP Create notification" to "social media post" in its most limited form:
- You send a `Create` where the `object` is a `Note`.
- The object MUST have `content` whose value is a string.
- The content MUST conform to some sanitized subset of HTML.
- The object MUST have a `published` whose value is a string containing a timestamp.
- The object MUST have an `attributedTo` whose value is exactly one actor.
- The actor MUST be of type `Person`.
- The actor MUST have a `name` whose value is a string.
- The name MUST be plain text, and will be interpreted as plain text.
- The actor MUST have an `icon` whose value is the URL to a 1:1 image file.
- The actor MUST have a `preferredUsername` whose value is a string.
- The username MUST be unique on the domain.
- The username MUST link back to the actor when querying that actor's domain's WebFinger endpoint for an `acct:` URI formed of the username and the domain.
- The object MUST have `to` or `cc` or both, whose value is an array which contains at least `https://www.w3.org/ns/activitystreams#Public`.
Now, I'm simplifying a bit, so the above has the following caveats:
- Requirements can be relaxed or willfully violated as various needs arise. For example, if an actor is missing an `icon`, the protocol can be modified such that a "default avatar" is assumed instead.
- Requirements can also be added or extended as the protocol matures and develops. For example, support for `inReplyTo`, `attachment`, `tag` can be added over time.
- Protocols may require other protocols or specifications. For example, WebFinger in the above "social media post" protocol.
- Additional protocols would be need to be defined for each use case. For example, a similar protocol for "making a blog post" could be defined using `Create Article`.
- Specifications ideally ought to be followed, but ultimately the real protocol is what implementers end up doing, and the above protocol codifies some mistakes and errors into itself. For example, hard assumptions of actor and object types, hard requirements on the maximum cardinality of properties like `attributedTo`, soft requirements for specifically `https://www.w3.org/ns/activitystreams#Public` instead of `as:Public`. (All of these things represent real incorrect assumptions by real implementers.)
When you put it like this, it becomes clear that if the fediverse wants to continue to make use of ActivityPub, then there are a *lot* of things that need to be explicitly defined as protocols layered[^protocol-layering] on top of ActivityPub. The "implicit protocol of the fediverse" is not sufficiently described by "the ActivityPub specification". We need to standardize and align on common patterns and common protocols for those patterns.
And the challenge here, the issue is, that if we don't do this work at a grassroots level, then Meta will do it for us. (Can you imagine a "developer portal" for "interoperating with Threads"?) Things like the FEP process allow writing up these "micro-protocols", and then after that, the difficult task of cat-herding begins, as you need to get implementers onboard with adhering to these specifications. And of course, we're all mostly doing this in our free time and without access to the resources that larger organizations have or can gain access to. So it's going to be an uphill battle. But whichever "protocol building blocks" we can agree and align upon, let's go for it.
[^multi-proto-approach]: I was specifically looking at a "unified communication" project, which mapped abstract forms of communication like "messaging", "publishing", "discussing", "reading" onto existing protocols like SMTP, XMPP, ActivityPub, IRC, RSS, ATOM, HTTP. There's a risk of ending up with something like Pidgin here, but maybe that's not the worst thing.
[^protocol-layering]: And of course, other protocols and other layerings are possible. Libervia (formerly Salut a Toi) does AS2 with PubSub over XMPP, but offers an "ActivityPub gateway". You could, if you wanted to, send AS2 over an SMTP transport.

View file

@ -1,26 +0,0 @@
+++
title = "multiple accounts"
summary = "why/when do people use multiple accounts, and what features can be implemented to reduce the friction in using just one account?"
date = "2017-12-17T03:55:00-06:00"
source = "https://mastodon.social/@trwnh/99189033326279405"
+++
unrelated thought: been trying to hash out a solution to the "multiple accounts" issue with social media. namely: why/when do people use multiple accounts, and what features can be implemented to reduce the friction in using just one account? would appreciate feedback.
off the top of my head:
- private vs public accounts for trusted people (answered by privacy options on toots)
- multiple interests (not really solved currently; perhaps implementing a tag system and letting people mute certain tags? Diaspora*-style aspects can get complicated)
- separate identity (unsolvable and would be insecure to attempt with one account)
wrt multiple interests, this really is my biggest pain point with ANY social media (coming from someone who has had like 15 birdsite accounts at once)
perhaps not exactly tags, but having a category system like google+ would be nice and perhaps easiest to manage when considering tootboosts. but this also might complicate the issue? nevertheless, it could be optional (default behavior would be to boost to your profile, no categorization involved)
the tag approach would be easiest for your own toots, but the categories wouldn't be too bad and would also allow for separating different boosts
---
## further discussion
problem with diaspora*'s aspects (and by extension, Google+ circles which aped it) is that it's a bidirectional metaphor for a unidirectional relationship. you're supposed to pick who can see a post, but they might not even follow you. I would understand if instead it functioned and was advertised as a way to split your timeline into multiple timelines. As is, sharing to aspects/circles/etc. is needlessly confusing.

View file

@ -1,5 +0,0 @@
oh right i forgot we live in a geopolitical climate of tens of thousands of digital nation-states each with their own culture and borders. for a second there i thought i was using a general communication service
anarchist, but against fediverse "instances"
the dilemma of fedi is that i don't want to host my own service using current software, but i don't want to be judged by whose service i use or by who else uses that service

View file

@ -1,14 +0,0 @@
+++
title = "necromancy"
summary = "\"i saw the soul of my grandpa and he told me to keep working hard\" is almost mystic and folkloric, not really like \"i summoned the soul of my grandpa to give me encouragement\" and still one step removed from \"i inserted my grandpa's soul into a corpse / some bones to give him physical form\""
date = "2018-04-12T07:31:00-00:00"
source = "https://mastodon.social/@trwnh/99845293118930539"
+++
i always have to stop and realize people don't mean actual necromancy (learning from the dead) and instead they mean some kinda pseudo-resurrection/reanimation thing in pop culture, haha
> honestly, is divination through dead people's spirits any better?
there's not as much stigma against it, compared to the physical. "i saw the soul of my grandpa and he told me to keep working hard" is almost mystic and folkloric, not really like "i summoned the soul of my grandpa to give me encouragement" and still one step removed from "i inserted my grandpa's soul into a corpse / some bones to give him physical form"
it seems like souls are free to either stay in heaven or roam the land of the living, but giving them physical form is like playing god

View file

@ -1,18 +0,0 @@
+++
title = "what is neoliberalism"
summary = "in short, everything is a market and the experts should decide the best policies to help the market"
date = "2018-03-14T08:10:00-00:00"
source = "https://mastodon.social/@trwnh/99681239150233207"
+++
> What is #neoliberalism? Is it a set defined thing or a term just like liberalism that could mean lots of different things to different people?
kinda both? liberalism and neoliberalism have academic meanings, but get used differently by different people
ie neoliberalism is just largely a return to liberalism; the idea that govt's role is to help business, that markets can be used as a force for good https://en.wikipedia.org/wiki/Neoliberalism
maybe best exemplified by thatcher, reagan, greenspan, clinton; the support of "free trade" agreements like NAFTA and TPP or organizations like the IMF or WTO; opposition to people like ralph nader in the 1970s; concepts like "the marketplace of ideas" or "soft power"; in short, everything is a market and the experts should decide the best policies to help the market
> [...]
Neoliberalism seems like a big tent, but that's really only because it's ideologically sparse and lacks substance. Most of the Western world holds a neoliberal economic and domestic policy, and a neoconservative foreign policy.

View file

@ -1,37 +0,0 @@
+++
title = "the day net neutrality was repealed"
summary = "The Open Internet Order of 2015 which was just repealed was not too much. It was not enough."
date = "2017-12-15T01:35:00-00:00"
source = [
"https://mastodon.social/@trwnh/99175742733649566",
"https://mastodon.social/@trwnh/99175781630692764",
]
+++
> [...]
Hey, remember when Comcast filtered P2P traffic including BitTorrent, Skype, and Spotify?
Remember when AT&T blocked FaceTime and Google Voice on their network?
Remember when Verizon throttled all video except mysteriously for their subsidiary go90?
Because I remember.
[Is it] a GOOD thing to have no legal authority to challenge ISPs when they pull the shit they clearly already did?
Because that's literally all that happened in 2015, w/ forbearance
In fact, Wheeler ONLY pursued the clauses of Title II that dealt with nondiscrimination (common carrier status), and didn't apply rate regulation or taxes or last-mile unbundling.
He did this because Verizon successfully sued to prevent the FCC from using S706, and the judge told Wheeler he needed to reclassify as Title II.
It should be obvious that the "free market" will not solve a problem created by the market. This is market failure, plain and simple.
> [...]
Which rules "allow" ISPs to monopolize regions? ISPs collude to not enter markets, renege on coverage expansion contracts, and prevent competition by claiming ownership of the backbone.
In fact, I would support taking it out of the FCC's hands entirely, so that people like Pai can't take us backwards at their whims as the political winds change. The Open Internet Order of 2015 which was just repealed was not too much. It was not enough. There needs to be an act specifically protecting the free and open internet, and repealing the Telecommunications Act of 1996. Among other acts...
So pardon me for thinking the decision today was not great. There's an overwhelming pile of evidence that Pai ignored wholesale in order to push an agenda that benefits the ISPs who simply want to profit more, users be damned.

View file

@ -1,42 +0,0 @@
+++
title = "a thematic breakdown of one piece"
summary = "east blue = dreams. paradise = family. new world = liberation."
date = "2024-09-30T11:22:54-0600"
source = "original"
+++
i was idly thinking about one piece this morning, as one does. mainly thinking about how east blue was peak one piece for me, and those first 100 chapters could honestly stand as their own self-contained story. one piece could have ended right there and i would have come away satisfied with what i read. but of course, it didn't end there, and i'm also glad it didn't end there. but it got me thinking about the overall theme of one piece, and specifically the overall theme of east blue as a self-contained saga.
<aside>
(this article generally contains spoilers for the entirety of one piece. read on if you dare or have already read one piece or just don't care. but if you don't care then why are you reading this)
</aside>
## from "romance dawn" onward to the "grand line"
honestly, we could go further and say that the first chapter of one piece is also a self-contained story, and in fact, it actually *was* a self-contained story. romance dawn was of course released in various forms as one-shots, before it eventually became the first chapter of one piece.
the story of that first chapter is very simple: it's about a boy with a dream. but it's everything else about the story that grips you, like the reason for that dream, and the magnitude of that dream, and what that dream involves.
for the time spent in east blue, the story mainly covers a great adventure filled with romanticism. the character arcs in this part of the story are mainly centered around following your dreams. as you meet new characters, you learn about their dreams, and in learning about their dreams, you get to know them as people. everyone has a reason to keep going. everyone has drive and motivation. it's honestly a dynamic world that feels alive, even though it's pretty small at this point in the series.
throughout it all, there is the end goal of entering the grand line, which is portrayed as a paradise for pirates, where wacky things happen all the time, and the adventure never ends. everyone has their reasons for seeking it out. luffy wants to become the pirate king, and to do that he needs to find the great treasure "one piece" at the end of the grand line. zoro wants to become the world's greatest swordsman, and to do that he needs to challenge the current world's greatest swordsman who lives in the grand line. nami wants to draw a map of the world, and that means she'll need to enter the grand line at some point. usopp wants to be a brave warrior of the sea, and the grand line is just the thing to toughen him up. sanji wants to find the all blue, which might exist somewhere in the grand line.
## i still have my nakama
once things go into the grand line, the theme of adventure is still there, but the focus of each arc is better summarized by the theme of "family". chopper is abandoned by his reindeer family, but finds new family in dr. hiruluk and later dr. kureha before eventually joining the crew. vivi knows that the straw hats will always be her family because of the whole x thing. robin finds family in the ohara researchers and in saul, only for the buster call to take it all. franky has tom and iceberg and later the franky family. brook's entire crew dies and his dream is to reunite with laboon. luffy's entire crew gets sent flying by kuma and he despairs over this because he lost his family. then he finds out his sworn brother is set to be executed, and he goes to save him. when he loses his brother he feels like he has no family left. until he is reminded that he still has his crew.
## power, authority, and liberation
by the time we enter the new world, it's endgame territory. this is the realm of the emperors. luffy has always been a liberator, but this part of the story is where it takes center stage. every arc has luffy liberating people and islands. and then you find out he's literally the warrior of liberation.
it's all about the power struggle, the emperors who rule their territories, the world government who controls everything, and the warrior of liberation who is being set up to set the whole world free.
## wealth. fame. power. he had it all
in that order.
## so what's the conclusion here
idk i don't have a conclusion i'm just rambling

View file

@ -1,6 +0,0 @@
+++
title = "physical bodies are so encumbering"
summary = "it would be great to never again have to feel pain from a stomach too empty, or perhaps too full; to never have crusty eyes after sleeping too much, or perhaps too little; to never suffer from physiology that at best will decay, and at worst will actively punish us."
date = "2018-05-09T14:42:00-00:00"
source = "https://mastodon.social/@trwnh/99999872996282760"
+++

View file

@ -1,12 +0,0 @@
+++
title = "product vs profit"
summary = "There will inevitably be a point where it is unprofitable to improve a product any further."
date = "2017-12-22T12:06:00-00:00"
source = "https://mastodon.social/@trwnh/99217860566004390"
+++
> maybe providing useful products turns a profit hmmm...
Only to a certain extent. There will inevitably be a point where it is unprofitable to improve a product any further.
i.e., if Apple's primary goal was to make useful products, it would make choices that result in a better product even if it was slightly more expensive. But their primary goal is profit, as it is for every corporation under capitalism. Making better stuff is unprofitable.

View file

@ -1,36 +0,0 @@
+++
title = "publishing vs discussing"
summary = "eventually this kind of \"posting\" we have today would be split up and merged into the two new things. the UI would give you an option: do you want to publish a Post, or do you want to start a Discussion?"
date = "2023-03-24T10:33:00-0600"
source = "https://mastodon.social/@trwnh/110078983668243890"
+++
i am more and more taking the position that we should have forums, and we should have blogs, and nothing in between. "social media" carries inherently poor ergonomics and a lack of clarity of purpose. you're never clearly "publishing" anything when you post on social media, unlike when you post on your own blog or website. you're never clearly "discussing" anything either, because threads aren't actual topics or conversations.
so my ideal flow is kinda like
- you make a "post" to your website/profile
- it may or may not be "in reply to" some other post. this is just metadata
- it may or may not be part of some "context". this is a moderated conversation or logical grouping owned by someone
- the "audience" is up to you, completely arbitrary. it might be only visible from within the context of some other webpage, or it might be given a permalink on your website/profile
fedi dropped the ball hard on those last two...
we could realistically move toward something more context-and-audience-aware but it's gonna be a bit rough for the existing "instance" model because of structural limitations. you'd basically be building a new fediverse in the shell of the old. but i think it can be done...
one prong is to support actual "publishing" with articles and a more fleshed-out web profile as a website. add webmentions as an optional thing.
the other prong is to support "discussing" within walls/forums/rooms "context"
eventually this kind of "posting" we have today would be split up and merged into the two new things. the UI would give you an option: do you want to publish a Post, or do you want to start a Discussion?
---
in my mental model. i have the following areas mapped out:
- Publishing (posts)
- Reading (feeds/timelines)
- Discussing (forums)
- Messaging (chats)
all of these are types of communication, and naturally some apps are going to expand to cover more areas. but it's all dependent on context and presentation. a "chat room" and a "forum topic" and a "social media wall" are all just collections of objects, really.

View file

@ -1,10 +0,0 @@
+++
title = "reading theory"
summary = "the point of reading theory is to see what ideas other people already came up with, so you don't have to spend time formulating them yourselves."
date = "2018-01-27T08:44:00-00:00"
source = "https://mastodon.social/@trwnh/99420908783601966"
+++
the point of reading theory is to see what ideas other people already came up with, so you don't have to spend time formulating them yourselves. like... you start out with political beliefs and experiences, but they can always be solidified if you have a framework to contextualize them.
and that doesn't really require discussion unless you want to or need more understanding, and it doesn't require organizing if you're not capable of it, and it really doesn't require voting since many people can't vote and many voting systems are designed to minimize choice. it feels wrong to say one *can't* be politically engaged unless one does these things

View file

@ -1,12 +0,0 @@
+++
title = "fully responsive web design"
summary = "my pipe dream is to redesign my website to be fully responsive -- not across mere pixel widths, but by aspect ratio and overall size"
date = "2018-04-22T15:42:00-00:00"
source = "https://mastodon.social/@trwnh/99903847448978099"
+++
my pipe dream is to redesign my website to be fully responsive -- not across mere pixel widths, but by aspect ratio and overall size.
does anyone consider what their website looks like when it's fullscreened across one of those samsung 3840x1080 monitors? probably not, and why would you?
but i do

View file

@ -1,24 +0,0 @@
+++
title = "services aren't communities"
summary = "imo the biggest mistake of fedi is tying together the social and technical layers [...] some people want services and some people want communities and they are not the same thing"
date = "2023-05-07T17:39:00-06:00"
source = "https://mastodon.social/@trwnh/110329802576726213"
+++
services aren't communities
the gmail community
imo the biggest mistake of fedi is tying together the social and technical layers. local timelines should have been group chats all along
it's a huge misunderstanding because some people want services and some people want communities and they are not the same thing. some people can run a community but offer bad service. some people offer great service but can't run a community
instances shouldn't exist as the only model. the service layer and the community layer should be separate. combining the two makes both of them worse -- as can be seen if you violate the rules of your community, you lose access not just to the community, but to the service entirely. and if the community cannot continue to provide service, you again lose both.
i do think there is some value in being both, because then people feel more likely to donate as they are personally invested. but you could have incentives to donate even while keeping them separate.
---
> Yes! I think I've been spiralling towards a less well articulated version of this for a while. Too many people have been trying to make the Fediverse just one big social network with many servers and erase the aspect that's many small social networks that can communicate, and it's exactly that they're viewing it purely as service and not as community, and it absolutely needs to be both or else it can't meaningfully be either.
i have a slightly different view, which is that multiple communities exist here and their organization and distribution is very dysfunctional because of the "instance" model being both and neither at the same time

View file

@ -1,8 +0,0 @@
+++
title = "shorts are too long"
summary = "if you're going to take away the video controls, prevent me from seeking, maybe autoloop? then i am NOT sitting there for a whole minute while you fail to get to the point."
date = "2023-01-25T08:40:00-06:00"
source = "https://mastodon.social/@trwnh/109750363384335885"
+++
i fucking hate the "shorts" format. vine was okay purely because it was limited to 6 or 7 seconds -- if you're going to take away the video controls, prevent me from seeking, maybe autoloop? then i am NOT sitting there for a whole minute while you fail to get to the point. i'm not doing more than 10 seconds, maybe 15 seconds at the most. anything over that should let me seek through the video!!!! videos should always be seekable but if you're not going to make them seekable then at least limit them to one coherent logical "moment"

View file

@ -1,26 +0,0 @@
+++
title = "i hate the smartphone market"
summary = "profit motives means that the entire market has abandoned me"
date = "2018-05-07T08:13:00-00:00"
source = "https://mastodon.social/@trwnh/99987017261137486"
+++
tbh like 90% of my day-to-day angst against capitalism is from how shit the smartphone market has become. if capitalism is so great then where the fuck is my <70mm-wide phone with a removable battery? fucking profit motives means that the entire market has abandoned me. I haven't been satisfied with any phone since 2010. hell, I'd buy an updated Nexus One with newer, more efficient internals.
"capitalism brought you your smartphone!" yeah, and that phone /fucking sucks/.
I wish I could build my own phone or have one built to spec, but alas, I do not own a machining and assembly factory, and I don't have the requisite circuitry knowledge... if only I could, I dunno, *cooperate* with some like-minded people, and we had *public ownership of those factories*... :thaenkin:
> do you know about this company? https://www.fairphone.com/en/ . They do their best to source all their components ethically, and work with worker's organisations in the countries they are producing phones in. The fair phone also comes with root access by default, so you can install your own ROM if you want.
yeah; they're well-intentioned but still restricted to operating within the confines of capitalism. and it's still too big of a phone for my liking, personally
like, i'm using a sony xperia x compact right now because it's 4.6" and fits in one hand. the snapdragon 650 means the battery is *decent*, but for how long? i got it lightly used in september, and now it's may and the battery is starting to heat up and degrade after 8 months of heavy usage and all those recharge cycles.
> my fair phone 1 is still working nicely after 3 years or so [...]
I feel like the same could be said about several more devices from 2013 than could be said today. my mom is still using a Nexus 7 2013 tablet just fine. our Nexus 5 could have lasted until now, if she hadn't broken it accidentally in a fit of rage. my htc one m7 is still close to the "ideal" device for me, except its battery has long gone to shit and is a nightmare to replace.
I wish there were like 400 more fairphones, because just 1 is too limited to accomplish much for my case. and part of why there's only 1, I imagine, is because they can't be profiting too much; they still rely on profit, don't they? It's not "sustainable" for companies to provide services at-cost under capitalism.
[...] it's extremely hard to "build it yourself" at sufficient scale to make a difference... and even then, you're bound by the laws of capital. you need to make money to survive, so at-cost operation is not viable without external funding

View file

@ -1,14 +0,0 @@
+++
title = "social media is fundamentally flawed"
summary = "in pure terms you might send a message or publish a resource. but with these social networks [...] you're never unambiguously sending a message or publishing a resource, but instead, some weird hybrid that we call a \"post\". not quite message, not quite resource"
date = "2023-02-16T19:43:00-06:00"
source = "https://mastodon.social/@trwnh/109877540362444317"
+++
the more i think about and research communication paradigms, i'm starting to think more and more that this whole "social media" thing is just fundamentally flawed. in pure terms you might send a message or publish a resource. but with these social media platforms it's not that clear cut. you're never unambiguously sending a message or publishing a resource, but instead, some weird hybrid that we call a "post". not quite message, not quite resource. it exists in part or in whole exclusively on the platform.
and i guess that makes it easier to put in a silo which is how companies maintain their profit motive.
but in replicating the design of these social media sites we replicate their properties too. fedi hasn't entirely moved past silos because it's embedded in the design level. thankfully not the protocol level, but still. it's going to persist in implementations as long as we limit ourselves to "twitter but better", "instagram but better", etc. we're not building the commons that we could build.
and the lack of clarity in our metaphors and associated abstractions leads to subtle violations of what people expect. there should be a clearer split. consider the duality of tumblr as both a social network and a publishing platform. they're making this split more apparent than it used to be. in addition to username.tumblr.com for blogs they now have tumblr.com/username for network profiles.

View file

@ -1,40 +0,0 @@
+++
title = "specs aren't enough"
summary = "the question of \"interoperability\" makes no sense on its own. you must always ask, interoperability with what? and on which conditions? [...] is your view of the \"fediverse\" limited only to the conceptual space of existing projects? and, if so, then which ones?"
date = "2023-03-06T18:37:00-06:00"
source = "https://mastodon.social/@trwnh/109979200684979970"
+++
@ people who keep saying we should rewrite the activitypub spec to ensure better interoperability with existing fedi projects
![](specs-are-enough.png)
idk how else to say that if you want to be compatible with a project then you need to have a shared worldview and conceptual space, and this necessarily involves communicating with that project on some level (by reading their documentation, asking their devs questions, etc) in order to know what their expectations and requirements are
the question of "interoperability" makes no sense on its own. you must always ask, interoperability with what? and on which conditions?
like, we all use activitypub more or less according to the spec, and that's because the spec is simple, it's basically just "HTTP POST to inbox". we could theoretically construct any as2 payload we wanted, but we largely limit ourselves to Create Note... why? because you want to be compatible with mastodon, right? and that's the real issue.
there are things that mastodon et al do that are within some spec -- webfinger for translating usernames to actors, for example. then there are things that are technically a spec but outdated, like http signatures (cavage draft) and ld signatures (obsoleted entirely). and then there are things that are not specced -- quirks of the implementation that must be respected, things like using Note, how to structure a Mention, the use of specific algorithms for signatures. maybe these can be negotiated
crucially, sometimes the assumptions you make, the quirks your software has, these will not line up with the assumptions that others make, the quirks that their software has. you might even disagree entirely, and without the possibility of reconciling those opposing assumptions.
consider the assumption that every actor has a username, and that this username identifies them more canonically than their own activitypub identifier. what if you disagree? well, that's an incompatibility.
how do you handle the root of identity being the activitypub id, versus the root of identity being the username, versus the root of identity being some cryptographic key? what is one to do, if one wants to be "compatible" and "interoperate" with three different softwares each following a different one of the above assumptions about identity? are these softwares even meaningfully part of the same network? and which of these three networks is the "true" "fediverse"?
and even within those identity schemes, we might still have to consider further parameters, like the expectation of only supporting HTTPS URIs. or, let's imagine that we choose to use or support DIDs -- now, which DID methods do we support?
all of these are decisions that each project has to either make for itself, or vest authority in some other entity to make those decisions in a binding way -- by publishing some meta-spec, some conformance profile, basically a bundle of specs that should be followed and their particulars.
and even *then*, you will find some quirks remain, some projects are coded in a way which has implicit expectations. the more you try to eliminate these, the narrower the scope or conceptual space becomes.
with enough limitations, you end up with what is basically the same software, just with different implementations. if mastodon, pleroma, and misskey all were overspecced like this, you may as well view them as "fedi implemented in rails", "fedi implemented in elixir", and "fedi implemented in nodejs".
so, in closing, i leave with this to consider:
is your view of the "fediverse" limited only to the conceptual space of existing projects? and, if so, then which ones?
or will you allow yourself to dream a little bigger?
{{<youtube WcGbnX8Ay38>}}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 KiB

View file

@ -1,18 +0,0 @@
+++
title = "syndicalism in the 21st century"
summary = "syndicalism is dead because the labor industry is dead. it's been replaced by the service industry today"
date = "2018-01-03T19:42:00-06:00"
source = "https://mastodon.social/@trwnh/99289017794408922"
+++
> Is syndicalism relevant in the 21st century?
there's a reason why crimethinc call themselves an "ex"-worker collective. syndicalism is dead because the labor industry is dead. it's been replaced by the service industry today. factories are either on the decline or they're being exported. so i guess in that sense, syndicalism can still live in china, or wherever else production is a significant part of the economy so that a general strike can bring it to its knees
or, alternatively, i suppose all food servers and retail employees could go on strike but the capitalist powers have created an abundance of unemployed people ready to bust that strike
i see it as much more likely that the masses will revolt wholesale, before any trade-unionist path could ever make itself viable again
very likely within the next 20 years, and almost certainly by the end of the century. too many crises are coming to a head "by 2040, 2050, 2080, 2100" etc. natural and anthropogenic conflicts will arise more easily than ever before
to be clear: organizing workers is still the most effective way forward; unionism can help but isn't an effective avenue for change anymore compared to agitation. but there's only so much we can gain by lobbying for labor standards; we need to push for fundamental transformation of the entirety of class relations

View file

@ -1,15 +0,0 @@
+++
title = "the web is not only for profit"
summary = "i just want to find some inspiration on how to express myself, not market myself"
date = "2023-01-03T01:58:00-06:00"
source = "https://mastodon.social/@trwnh/109624210338690804"
+++
it's funny how a lot of help articles can be boiled down to one of
a) it's really easy, just give us money
b) it's really hard, so give us money
people act like the only worthwhile use case of the web is financial gain. no one has any tips or hints on how to make a personal website, it's always "build your brand" or "showcase your portfolio" or some other transparently profit-motivated initiative. everyone is always talking about the sales funnel or the call to action or whatever. it's just *assumed* you are also like this.
i just want to find some inspiration on how to express myself, not market myself

View file

@ -1,10 +0,0 @@
+++
title = "the problem with universal basic income"
summary = "market forces on basic needs need to be abolished"
date = "2018-03-16T02:00:00-00:00"
source = "https://mastodon.social/@trwnh/99691112300163044"
+++
my only problem with UBI is if you're going to guarantee a basic income then why not just guarantee the basics and cut down on the bureacracy? also you can't just give everyone a lump sum of money and not expect a market to react to that flood of monetary supply -- market forces on basic needs need to be abolished
tldr UBI is a bandage on capitalism in the same way the New Deal was, but even less accountable

View file

@ -1,12 +0,0 @@
+++
title = "voluntarism vs socialism"
summary = "If two people are inequal, then they can never come to a voluntary agreement -- the power of one will coerce the other"
date = "2018-04-06T23:19:00-00:00"
source = "https://mastodon.social/@trwnh/99815047195372515"
+++
My point is that pursuing pure voluntarism as an ideology (and not simply an ideal) tends to ignore the realities of power imbalances. If two people are inequal, then they can never come to a voluntary agreement -- the power of one will coerce the other, even if subconsciously.
Consider whether one can "voluntarily" become a slave. Or, more relevant today, consider the wage labor system. Is it truly "voluntary" if your food, shelter, and very survival depends on such a "voluntary" arrangement?
The cold reality is that voluntarism is merely idealism so long as any social class or hierarchy exists. Maybe some prefigurative politics are necessary, but you have to push for progress without 100% support or you'll wait forever.

View file

@ -1,21 +0,0 @@
+++
title = "http uri maximalism"
summary = "if you just wanna obtain a resource from a given organization or whatever, it is generally a given that the org has its own domain name and can assign ids on that domain. but the whole thing falls apart when you need a global namespace that can be requested from multiple domains"
date = "2023-02-18T09:20:00-06:00"
source = "https://mastodon.social/@trwnh/109886416964217719"
+++
reading a lot of old w3 literature and while there's a lot of good stuff in there, it is *infuriating* how they are so blindly in support of using http for everything, including identifiers. they probably just failed to predict the future, but they keep stopping just short of admitting http's biggest problem: when the server goes down, your uri can't be dereferenced anymore!
(specifically i was reading https://www.w3.org/2001/tag/doc/URNsAndRegistries-50.xml which argues against urns because you can't dereference them)
am i being too harsh on an editorial from 2001, sure, yeah, probably no one was seriously thinking of decentralized systems back then. if you just wanna obtain a resource from a given organization or whatever, it is generally a given that the org has its own domain name and can assign ids on that domain. but the whole thing falls apart when you need a global namespace that can be requested from multiple domains. this is *exactly* where urns are useful! like, naming things is in some way id!
if we follow the logic of http maximalists from 2001, then we would not need the isbn or doi namespaces, we could just assign identifiers on an http hostname. a doi can be resolved using an http url so why not just use the url directly?
well, that assumes persistence that simply isn't guaranteed. the doi foundation will tell you itself: https://www.doi.org/the-identifier/resources/factsheets/doi-system-and-persistent-urls
> PURLs are all http and inherit both the strength and weakness of that approach
it's those weaknesses that kept being ignored

View file

@ -1,4 +0,0 @@
#!/bin/bash
rm -r public/
hugo
rsync -avz --delete public/ a@trwnh.com:/srv/http/trwnh.com/blog

View file

@ -1,9 +0,0 @@
baseURL = 'https://trwnh.com/blog/'
languageCode = 'en-us'
title = '~a blog'
markup.goldmark.renderer.unsafe = true
pagination.pagerSize = 100
[outputs]
home = ["HTML", "JSON"]

View file

@ -1,60 +0,0 @@
<!DOCTYPE html>
<html lang="{{.Site.Language.Lang }}" xml:lang="{{.Site.Language.Lang }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
{{- /* print layout */ -}}
{{- $print := resources.Get "styles/print.scss" | toCSS | minify | fingerprint }}
<link rel="stylesheet"
href="{{ $print.Permalink }}"
integrity="{{ $print.Data.Integrity }}"
media="print" />
{{- /* web layout */ -}}
{{- $theme := resources.Get "styles/screen.scss" | toCSS }}
{{- with resources.Get "styles/custom.scss" }}
{{- $custom := . | toCSS }}
{{- $screen := slice $theme $custom | resources.Concat "assets/screen.css" | minify | fingerprint }}
<link rel="stylesheet"
href="{{ $screen.Permalink }}"
integrity="{{ $screen.Data.Integrity }}"
media="screen" />
{{- else }}
{{- $screen := $theme | minify | fingerprint }}
<link rel="stylesheet"
href="{{ $screen.Permalink }}"
integrity="{{ $screen.Data.Integrity }}"
media="screen" />
{{- end }}
{{- /* scripts */ -}}
{{- $theme := resources.Get "scripts/main.js" | js.Build "script.js" | minify | fingerprint }}
{{ with resources.Get "scripts/custom.js" }}
{{ $custom := . }}
{{ $script := slice $theme $custom | resources.Concat "assets/main.js" | js.Build "script.js" | minify | fingerprint}}
<script type="text/javascript"
src="{{ $script.Permalink }}"
integrity="{{ $script.Data.Integrity }}">
</script>
{{ else }}
{{ $script := $theme | js.Build "script.js" | minify | fingerprint}}
<script type="text/javascript"
src="{{ $script.Permalink }}"
integrity="{{ $script.Data.Integrity }}">
</script>
{{ end }}
{{ partial "seo.html" . -}}
{{- block "head" . -}}
{{ end }}
</head>
<body>
{{ partial "site-header.html" . }}
{{ block "main" . }}
{{ end }}
{{ partial "site-footer.html" . }}
</body>
</html>

View file

@ -1,14 +0,0 @@
{{ define "main" }}
<main class="_default-list">
<header class="page-header section">
<div class="container">
<h1 class="page-title">{{.Title}}</h1>
</div>
</header>
<section class="pages section">
<div class="container">
{{ partial "paginator.html" . }}
</div>
</section>
</main>
{{ end }}

View file

@ -1,26 +0,0 @@
{{ define "head" }}
{{ partial "search/search-index.html" . }}
{{ end }}
{{ define "main" }}
<main>
<div class="search-results section">
<header>
<div class="container">
{{ partial "search/search-form.html" . }}
<h2 class="search-results__title"><span id="results-count"></span> <span id="results-count-text"></span> for "<span id="results-query"></span>"</h2>
</div>
</header>
<section>
<div class="container">
<ul id="search-results">
</ul>
</div>
</section>
</div>
</main>
<style>
a[href="#top"] {display: none;}
</style>
{{ end }}

View file

@ -1,24 +0,0 @@
{{ define "main" }}
<main class="_default-single">
<article class="page h-entry hentry" {{- if .Param "autonumbering" }} autonumbering {{- end }}>
<header class="section page-header">
<div class="container">
<h1 class="page-title p-name entry-title">{{ .Title | safeHTML }}</h1>
<p class="page-summary p-summary entry-summary">{{.Summary | safeHTML}}</p>
<p class="page-date">
<time class="dt-published published" datetime='{{.Date.UTC.Format "2006-01-02T15:04:05Z0700"}}'>{{ .Date.Format "Mon Jan 2, 2006" }}</datetime>
</p>
<div class="page-author h-card vcard">
<a href="https://trwnh.com" class="u-url p-name p-nickname url fn nickname">a</a>
</div>
</div>
<hr>
</header>
<section class="page-content section">
<div class="container e-content entry-content">
{{ .Content }}
</div>
</section>
</article>
</main>
{{ end }}

View file

@ -1,9 +0,0 @@
{{ define "main" }}
<main class="index">
<section class="pages">
<div class="container">
{{ partial "paginator.html" . }}
</div>
</section>
</main>
{{ end }}

View file

@ -1,8 +0,0 @@
{{- $.Scratch.Add "pagesIndex" slice -}}
{{- range $index, $page := .Site.RegularPages -}}
{{- if gt (len $page.Content) 0 -}}
{{- $pageData := (dict "title" $page.Title "href" $page.Permalink "content" $page.Plain) -}}
{{- $.Scratch.Add "pagesIndex" $pageData -}}
{{- end -}}
{{- end -}}
{{- $.Scratch.Get "pagesIndex" | jsonify -}}

View file

@ -1,15 +0,0 @@
<nav class="header-nav">
<ul class="menu">
{{ $currentPage := . }}
{{ range .Site.Menus.main }}
<li
class='{{ .Identifier }}{{ if or ($currentPage.IsMenuCurrent "main" .) (eq $currentPage.Section .Identifier) }}active{{ end }}'>
<a href="{{ .URL | relLangURL }}">
{{ .Pre }}
<span>{{ .Name }}</span>
{{ .Post }}
</a>
</li>
{{ end }}
</ul>
</nav>

View file

@ -1,16 +0,0 @@
<div class="paginator">
<div class="list">
{{ $paginator := .Paginate .Site.RegularPages }}
{{ range $paginator.Pages }}
<article class="page">
<p>
{{.Date.Format "2006-01-02"}}:
<a href="{{.Permalink}}">{{.Title}}</a><br>
<span>{{.Summary}}</span>
</p>
</article>
{{ end }}
</div>
<!-- Hugo's default pagination template. -->
{{ template "_internal/pagination.html" . }}
</div>

View file

@ -1,5 +0,0 @@
<form id="search-form" action='{{ with .GetPage "/search" }}{{.Permalink}}{{end}}' method="get">
<label hidden for="search-input">Search site</label>
<input id="search-input" type="text" name="query" placeholder="Type here to search">
<input id="search-submit" type="submit" value="search">
</form>

View file

@ -1,4 +0,0 @@
<script src="https://unpkg.com/lunr/lunr.js"></script>
{{ $js := resources.Get "scripts/search.js" | minify | fingerprint }}
<script defer type="text/javascript" src="{{ $js.Permalink }}" integrity="{{ $js.Data.Integrity }}"></script>
<div id="base-url" data-base-url="{{.Site.BaseURL}}"></div>

View file

@ -1,153 +0,0 @@
{{- $title := or .Title $.File.ContentBaseName }}
{{- $cover := ($.Resources.ByType "image").GetMatch "{*opengraph*}" -}}
{{ $icon := resources.GetMatch (default "" .Site.Params.icon) -}}
{{/*=== title ===*/}}
{{ "<!-- text -->" | safeHTML }}
{{- with .Site.Title }}
<meta name="application-name" property="og:site_name" content="{{ . }}" />
{{- end -}}
{{- if .IsHome}}
{{- with or .Params.name .Site.Title }}
<title>{{ . }}</title>
<meta property="og:title" name="twitter:title" itemprop="name" content="{{ . }}" />
{{- end }}
{{- else }}
<title>{{ or .Params.name (print $title " • " .Site.Title) }}</title>
<meta property="og:title" name="twitter:title" itemprop="name" content="{{ or .Params.name $title }}" />
{{- end -}}
{{/*=== description ===*/}}
{{- with or .Description .Summary .Site.Params.description }}
<meta name="description" itemprop="description" property="og:description" content="{{ . }}" />
<meta name="twitter:description" content="{{ . }}" />
{{- end }}
{{ "<!-- url -->" | safeHTML }}
{{- with .Permalink | absURL}}
<base href="{{ . }}" />
<link rel="canonical" href="{{ or $.Params.canonical . }}" />
<meta name="url" property="og:url" itemprop="url" content="{{ . }}" />
<meta name="twitter:url" content="{{ . }}" />
{{- end -}}
{{/*=== image ===*/}}
{{- $staticIcon := "icon.png" | absURL -}}
{{- with or $cover $icon }}
{{ "<!-- image -->" | safeHTML }}
<meta property="og:image" itemprop="image" content="{{ .Permalink | absURL }}" />
{{- with .Width }}
<meta property="og:image:width" content="{{ . }}" />
{{- end }}
{{- with .Height }}
<meta property="og:image:height" content="{{ . }}" />
{{- end }}
<meta name="twitter:image" content="{{ .Permalink | absURL }}" />
<meta name="twitter:image:src" content="{{ .Permalink | absURL }}" />
{{- else }}
{{ "<!-- image -->" | safeHTML }}
<meta property="og:image" itemprop="image" content="{{ $staticIcon }}" />
<meta name="twitter:image" content="{{ $staticIcon }}" />
<meta name="twitter:image:src" content="{{ $staticIcon }}" />
{{- end -}}
{{/*=== extra params? ===*/}}
{{- with .Params.audio }}<meta property="og:audio" content="{{ . }}" />{{ end }}
{{- with .Params.locale }}<meta property="og:locale" content="{{ . }}" />{{ end }}
{{- with .Params.videos }}{{- range . }}
<meta property="og:video" content="{{ . | absURL }}" />
{{ end }}{{ end }}
{{/*=== article ===*/}}
{{ with or .Params.author .Site.Params.author -}}
{{ "<!-- author -->" | safeHTML }}
<meta name="author" property="article:author" content="{{ . }}" />
{{- end }}
<meta property="article:publisher" content="{{ .Site.BaseURL }}" />
{{ "<!-- time -->" | safeHTML }}
{{ with .Date -}}
<meta property="article:published_time" itemprop="datePublished" content={{ .Format "2006-01-02T03:04:05Z" | safeHTML }} />
{{ end -}}
{{ with .Lastmod -}}
<meta property="article:modified_time" itemprop="dateModified" content={{ .Format "2006-01-02T03:04:05Z" | safeHTML }} />
{{ end -}}
{{/*=== section and keywords ===*/}}
{{- "<!-- keywords -->" | safeHTML }}
{{ with.Params.category -}}
<meta property="article:section" content="{{ . }}" />
{{- end -}}
{{- with .Params.tags }}
<meta property="article:tag" itemprop="keywords" name="keywords" content='{{ delimit . " "}}' />
{{- end -}}
{{- if isset .Params "date" }}
{{ "<!-- article metadata -->" | safeHTML }}
<meta property="og:type" content="article" />
<meta itemprop="wordCount" content="{{ .WordCount }}" />
<script defer type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "Article",
"headline": {{ $title }},
"author": {
"@type": "Person",
"name": {{ or .Params.author .Site.Params.author }},
"url": {{ .Site.BaseURL }}
},
"datePublished": {{ .Date.UTC.Format "2006-01-02T03:04:05Z" }},
"description": {{ (or .Description .Summary) | plainify }},
"wordCount": {{ .WordCount }},
"mainEntityOfPage": {{.Permalink}},
"dateModified": "{{ .Lastmod.UTC.Format "2006-01-02T03:04:05Z" }}",
"image": {
"@type": "ImageObject",
"url": {{ with or $cover $icon }}{{ .Permalink | absURL }}{{ end }}
},
"publisher": {
"@type": "WebSite",
"name": {{ .Site.Title }},
"url": {{ .Site.BaseURL }},
"image": {
"@type": "ImageObject",
"url": {{with $icon}}{{.Permalink}}{{else}}{{$staticIcon}}{{end}}
}
}
}
</script>
{{- else }}
{{ "<!-- webpage metadata -->" | safeHTML }}
<meta property="og:type" content="website" />
<script defer type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "WebPage",
"name": {{ .Title }},
"url": {{ .Permalink }},
"description": {{ (or .Description .Summary) | plainify }},
"image": {
"@type": "ImageObject",
"url": {{with $icon}}{{.Permalink}}{{else}}{{$staticIcon}}{{end}}
}
}
</script>
{{- end -}}
{{/* auxiliary info */}}
{{ "<!-- site presentation -->" | safeHTML }}
{{ with $icon }}
<link rel="shortcut icon" href='{{ .Permalink }}' sizes="{{.Width}}x{{.Height}}" />
{{- else -}}
<link rel="shortcut icon" href='{{ $staticIcon }}' sizes="512x512" />
{{- end }}
<meta name="theme-color" content="#ffffff" />
<meta name="msapplication-TileColor" content="#ffffff" />
<link rel="sitemap" type="application/xml" title="Sitemap" href="{{ .Site.BaseURL }}sitemap.xml" />
{{ with .OutputFormats.Get "RSS" -}}
<link href="{{ .Permalink }}" rel="feed alternate" type="application/rss+xml" title="{{ $.Site.Title }}" />
{{- end -}}
{{/* robots */}}
{{ "<!-- robots -->" | safeHTML }}
<meta name="robots" content="index,follow" />
<meta name="googlebot" content="index,follow" />

View file

@ -1,6 +0,0 @@
<footer class="site-footer">
<hr>
<div class="container">
<a href="#top">back to top</a>
</div>
</footer>

View file

@ -1,25 +0,0 @@
<header class="site-header">
<div class="container">
<div class='hang-left'>
<a href="{{.Site.BaseURL}}" class="site-masthead">
{{ with .Site.Params.masthead.image }}
<img class="site-masthead__image" height="32" src="{{.}}">
{{ end }}
{{ with resources.Get .Site.Params.masthead.svg }}
{{ .Content | safeHTML }}
{{ end }}
{{if .Site.Params.masthead.hideTitle}}
{{ else }}
<span class="site-title">{{or .Site.Params.masthead.title .Site.Title}}</span>
{{ end }}
</a>
</div>
{{ if in .Site.Params.search.showIn "header" }}
{{ partial "search/search-form.html" . }}
{{ end }}
{{ partial "nav-header.html" . }}
</div>
</header>
<div class="scroll-margin" style="position: relative;">
<div id="top" style="scroll-margin-top: var(--header-height);"></div>
</div>

View file

@ -1,6 +0,0 @@
<aside class="toc section">
<details open>
<summary class="toc-title">Contents</summary>
{{ .Page.TableOfContents }}
</details>
</aside>

View file

@ -1,9 +0,0 @@
+++
kind = "reply"
title = ""
summary = ""
date = "2018-08-01T00:00:00Z"
tags = []
inReplyTo = ""
original = ""
+++

View file

@ -1,7 +0,0 @@
+++
kind = "note"
tags = []
date = "2016-11-24T19:54:00Z"
attachment = []
original = ""
+++

View file

@ -1,15 +0,0 @@
/*
Use a window's inner dimensions for viewport units.
This fixes some mobile bugs
*/
var root = document.documentElement;
let vh = window.innerHeight * 0.01;
root.style.setProperty('--vh', `${vh}px`);
// We listen to the resize event
window.addEventListener('resize', () => {
// We execute the same script as before
let vh = window.innerHeight * 0.01;
root.style.setProperty('--vh', `${vh}px`);
});

View file

@ -1,225 +0,0 @@
/*
tutorials used:
- https://aaronluna.dev/blog/add-search-to-static-site-lunrjs-hugo-vanillajs/#codepen-with-final-code
- https://victoria.dev/blog/add-search-to-hugo-static-sites-with-lunr/
*/
let pagesIndex, searchIndex
const MAX_SUMMARY_LENGTH = 30
const SENTENCE_BOUNDARY_REGEX = /\b\.\s/gm
const WORD_REGEX = /\b(\w*)[\W|\s|\b]?/gm
async function initSearch() {
try {
const response = await fetch("/index.json");
pagesIndex = await response.json();
searchIndex = lunr(function () {
this.field("title");
this.field("content");
this.ref("href");
pagesIndex.forEach((page) => this.add(page));
});
} catch (e) {
console.log(e);
}
console.log("Search index initialized")
// Get the query parameter(s)
const params = new URLSearchParams(window.location.search)
const query = params.get('query')
// Perform a search if there is a query
if (query) {
// Retain the search input in the form when displaying results
document.getElementById('search-input').setAttribute('value', query)
// Update the list with results
console.log("search performed")
let results = searchSite(query)
renderSearchResults(query, results)
}
}
initSearch();
function searchSite(query) {
const originalQuery = query;
query = getLunrSearchQuery(query);
let results = getSearchResults(query);
return results.length
? results
: query !== originalQuery
? getSearchResults(originalQuery)
: [];
}
function getLunrSearchQuery(query) {
const searchTerms = query.split(" ");
if (searchTerms.length === 1) {
return query;
}
query = "";
for (const term of searchTerms) {
query += `+${term} `;
}
return query.trim();
}
function getSearchResults(query) {
return searchIndex.search(query).flatMap((hit) => {
if (hit.ref == "undefined") return [];
let pageMatch = pagesIndex.filter((page) => page.href === hit.ref)[0];
pageMatch.score = hit.score;
return [pageMatch];
});
}
function renderSearchResults(query, results) {
clearSearchResults();
updateSearchResults(query, results);
}
function clearSearchResults() {
const results = document.querySelector("#search-results");
while (results.firstChild) results.removeChild(results.firstChild);
}
function updateSearchResults(query, results) {
document.getElementById("results-query").innerHTML = query;
document.querySelector("#search-results").innerHTML = results
.map(
(hit) => `
<li class="search-result-item" data-score="${hit.score.toFixed(2)}">
<a href="${hit.href}" class="search-result-page-title">${createTitleBlurb(query, hit.title)}</a>
<p>${createSearchResultBlurb(query, hit.content)}</p>
</li>
`
)
.join("");
const searchResultListItems = document.querySelectorAll("#search-results li");
document.getElementById("results-count").innerHTML = searchResultListItems.length;
document.getElementById("results-count-text").innerHTML = searchResultListItems.length === 1 ? "result" : "results";
// searchResultListItems.forEach(
// (li) => (li.firstElementChild.style.color = getColorForSearchResult(li.dataset.score))
// );
}
function createTitleBlurb(query, title) {
const searchQueryRegex = new RegExp(createQueryStringRegex(query), "gmi");
return title.replace(
searchQueryRegex,
"<strong>$&</strong>"
)
}
function createSearchResultBlurb(query, pageContent) {
const searchQueryRegex = new RegExp(createQueryStringRegex(query), "gmi");
const searchQueryHits = Array.from(
pageContent.matchAll(searchQueryRegex),
(m) => m.index
);
const sentenceBoundaries = Array.from(
pageContent.matchAll(SENTENCE_BOUNDARY_REGEX),
(m) => m.index
);
let searchResultText = "";
let lastEndOfSentence = 0;
for (const hitLocation of searchQueryHits) {
if (hitLocation > lastEndOfSentence) {
for (let i = 0; i < sentenceBoundaries.length; i++) {
if (sentenceBoundaries[i] > hitLocation) {
const startOfSentence = i > 0 ? sentenceBoundaries[i - 1] + 1 : 0;
const endOfSentence = sentenceBoundaries[i];
lastEndOfSentence = endOfSentence;
parsedSentence = pageContent.slice(startOfSentence, endOfSentence).trim();
searchResultText += `${parsedSentence} ... `;
break;
}
}
}
const searchResultWords = tokenize(searchResultText);
const pageBreakers = searchResultWords.filter((word) => word.length > 50);
if (pageBreakers.length > 0) {
searchResultText = fixPageBreakers(searchResultText, pageBreakers);
}
if (searchResultWords.length >= MAX_SUMMARY_LENGTH) break;
}
return ellipsize(searchResultText, MAX_SUMMARY_LENGTH).replace(
searchQueryRegex,
"<strong>$&</strong>"
);
}
function createQueryStringRegex(query) {
const searchTerms = query.split(" ");
if (searchTerms.length == 1) {
return query;
}
query = "";
for (const term of searchTerms) {
query += `${term}|`;
}
query = query.slice(0, -1);
return `(${query})`;
}
function tokenize(input) {
const wordMatches = Array.from(input.matchAll(WORD_REGEX), (m) => m);
return wordMatches.map((m) => ({
word: m[0],
start: m.index,
end: m.index + m[0].length,
length: m[0].length,
}));
}
function fixPageBreakers(input, largeWords) {
largeWords.forEach((word) => {
const chunked = chunkify(word.word, 20);
input = input.replace(word.word, chunked);
});
return input;
}
function chunkify(input, chunkSize) {
let output = "";
let totalChunks = (input.length / chunkSize) | 0;
let lastChunkIsUneven = input.length % chunkSize > 0;
if (lastChunkIsUneven) {
totalChunks += 1;
}
for (let i = 0; i < totalChunks; i++) {
let start = i * chunkSize;
let end = start + chunkSize;
if (lastChunkIsUneven && i === totalChunks - 1) {
end = input.length;
}
output += input.slice(start, end) + " ";
}
return output;
}
function ellipsize(input, maxLength) {
const words = tokenize(input);
if (words.length <= maxLength) {
return input;
}
return input.slice(0, words[maxLength].end) + "...";
}
if (!String.prototype.matchAll) {
String.prototype.matchAll = function (regex) {
"use strict";
function ensureFlag(flags, flag) {
return flags.includes(flag) ? flags : flags + flag;
}
function* matchAll(str, regex) {
const localCopy = new RegExp(regex, ensureFlag(regex.flags, "g"));
let match;
while ((match = localCopy.exec(str))) {
match.index = localCopy.lastIndex - match[0].length;
yield match;
}
}
return matchAll(this, regex);
};
}

View file

@ -1,45 +0,0 @@
$site-max-width: 100rem;
:root {
--site-max-width: #{$site-max-width};
--nav-height: 4rem;
--header-height: 4.5rem;
--footer-height: 3rem;
}
html {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
scroll-behavior: smooth;
background: var(--ui-background);
color: var(--ui-text);
}
/* Single column layout, where main content stretches to fill. */
body {
display: flex;
flex-flow: column;
min-height: calc(var(--vh, 1vh) * 100);
max-width: 100vw;
margin: auto;
}
main {flex-grow: 1;}
/*
Sections are primary block units, usually of type <section>.
Containers are an immediate child <div>, purely for constraining width.
*/
.section {
padding: 2em 0; /* we apply a vertical padding only to sections */
}
.container {
width: 100%;
max-width: var(--site-max-width);
margin: 0 auto;
padding: 0 1em; /* and we apply a horizontal padding only to containers */
}
/* Ensure consistent colors for text selection */
::selection {
background: var(--primary-accent);
color: var(--primary-accent-text);
}

View file

@ -1,35 +0,0 @@
:root {
--link-color: #3371cf;
--link-visited: #594288;
--primary-accent: rgb(0, 123, 255);
--primary-accent-transparent: rgba(0, 123, 255,0.25);
--primary-accent-text: #fff;
--ui-background: #eee;
--ui-text: #111;
--ui-text-muted: #666;
--ui-text-bold: #000;
--ui-overlay: #fff;
--ui-overlay-text: var(--ui-text);
}
@media (prefers-color-scheme: dark) {
:root {
--link-color: #8fb1df;
--link-visited: #a089d4;
--primary-accent: rgb(0, 123, 255);
--primary-accent-transparent: rgba(64, 156, 255, 0.45);
--primary-accent-text: #fff;
--ui-background: #212121;
--ui-text: #eee;
--ui-text-muted: #999;
--ui-text-bold: #fff;
--ui-overlay: #333;
--ui-overlay-text: var(--ui-text);
}
}

Some files were not shown because too many files have changed in this diff Show more