@prologic@twtxt.net Regarding the new way of generating twt-hashes, to me it makes more sense to use tabs as separator instead of spaces, since the you can just copy/past a line directly from a twtxt-file that already go a tab between timestamp and message. But tabs might be hard to âtypeâ when you are in a terminal, since it will activate autocompleteâŚđ¤
Another thing, it seems that you sugget we only use the domain in the hash-creation and not the full path to the twtxt.txt
$ echo -e "https://example.com 2024-09-29T13:30:00Z Hello World!" | sha256sum - | awk '{ print $1 }' | base64 | head -c 12
Some more arguments for a local-based treading model over a content-based one:
The format:
(#<DATE URL>)or(@<DATE URL>)both makes sense: # as prefix is for a hashtag like we allredy got with the(#twthash)and @ as prefix denotes that this is mention of a specific post in a feed, and not just the feed in general. Using either can make implementation easier, since most clients already got this kind of filtering.Having something like
(#<DATE URL>)will also make mentions via webmetions for twtxt easier to implement, since there is no need for looking up the#twthash. This will also make it possible to make 3th part twt-mentions services.Supporting twt/webmentions will also increase discoverability as a way to know about both replies and feed mentions from feeds that you donât follow.
@falsifian@www.falsifian.org I believe the preserve means to include the original subject hash in the start of the twt such as (#somehash)
Sorry, youâre right, I should have used numbers!
Iâm donât understand what âpreserve the original hashâ could mean other than âmake sure thereâs still a twt in the feed with that hashâ. Maybe the text could be clarified somehow.
Iâm also not sure what you mean by markdown already being part of it. Of course people can already use Markdown, just like presumably nothing stopped people from using (twt subjects) before they were formally described. But itâs not universal; e.g. as a jenny user I just see the plain text.
@prologic@twtxt.net Thanks for writing that up!
I hope it can remain a living document (or sequence of draft revisions) for a good long time while we figure out how this stuff works in practice.
I am not sure how I feel about all this being done at once, vs. letting conventions arise.
For example, even today I could reply to twt abc1234 with â(#abc1234) Edit: âŚâ and I think all you humans would understand it as an edit to (#abc1234). Maybe eventually it would become a common enough convention that clients would start to support it explicitly.
Similarly we could just start using 11-digit hashes. We should iron out whether itâs sha256 or whatever but thereâs no need get all the other stuff right at the same time.
I have similar thoughts about how some users could try out location-based replies in a backward-compatible way (append the replyto: stuff after the legacy (#hash) style).
However I recognize that Iâm not the one implementing this stuff, and itâs less work to just have everything determined up front.
Misc comments (I havenât read the whole thing):
Did you mean to make hashes hexadecimal? You lose 11 bits that way compared to base32. Iâd suggest gaining 11 bits with base64 instead.
âClients MUST preserve the original hashâ â do you mean they MUST preserve the original twt?
Thanks for phrasing the bit about deletions so neutrally.
I donât like the MUST in âClients MUST follow the chain of reply-to referencesâŚâ. If someone writes a client as a 40-line shell script that requires the user to piece together the threading themselves, IMO we shouldnât declare the client non-conforming just because they didnât get to all the bells and whistles.
Similarly I donât like the MUST for user agents. For one thing, you might want to fetch a feed without revealing your identty. Also, it raises the bar for a minimal implementation (Iâm again thinking again of the 40-line shell script).
For âwho followsâ lists: why must the long, random tokens be only valid for a limited time? Do you have a scenario in mind where they could leak?
Why canât feeds be served over HTTP/1.0? Again, thinking about simple software. I recently tried implementing HTTP/1.1 and it wasnât too bad, but 1.0 would have been slightly simpler.
Why get into the nitty-gritty about caching headers? This seems like generic advice for HTTP servers and clients.
Iâm a little sad about other protocols being not recommended.
I donât know how I feel about including markdown. I donât mind too much that yarn users emit twts full of markdown, but Iâm more of a plain text kind of person. Also it adds to the length. I wonder if putting a separate document would make more sense; that would also help with the length.
Been thinking about it for the last couple of days and I would say we can make do with the shorter (#<DATETIME URL>)since it mirrors the twt-mention syntax and simply points to the OP as the topic identified by the time of posting it. Do we really need and (edit:...)and (delete:...) also?
@david@collantes.us Thanks, thatâs good feedback to have. I wonder to what extent this already exists in registry servers and yarn pods. I havenât really tried digging into the past in either one.
How interested would you be in changes in metadata and other comments in the feeds? Iâm thinking of just permanently saving every version of each twtxt file that gets pulled, not just the twts. It wouldnât be hard to do (though presenting the information in a sensible way is another matter). Compression should make storage a non-issue unless someone does something weird with their feed like shuffle the comments around every time I fetch it.
@movq@www.uninformativ.de I donât think it has to be like that. Just make sure the new version of the twt is always appended to your current feed, and have some convention for indicating itâs an edit and which twt it supersedes. Keep the original twt as-is (or delete it if you donât want new followers to see it); doesnât matter if itâs archived because you arenât changing that copy.
@prologic@twtxt.net Do you have a link to some past discussion?
Would the GDPR would apply to a one-person client like jenny? I seriously hope not. If someone asks me to delete an email they sent me, I donât think I have to honour that request, no matter how European they are.
I am really bothered by the idea that someone could force me to delete my private, personal record of my interactions with them. Would I have to delete my journal entries about them too if they asked?
Maybe a public-facing client like yarnd needs to consider this, but that also bothers me. I was actually thinking about making an Internet Archive style twtxt archiver, letting you explore past twts, including long-dead feeds, see edit histories, deleted twts, etc.
@prologic@twtxt.net Hi. i have noticed sometimes when i hit the back button i lose all the surrounding layout and just have a list of twts.

BTW this code doesnât incorporate existing twts into jennyâs database. Itâs best used starting from scratch. Iâve been testing it using a custom XDG_CACHE_HOME and XDG_CONFIG_HOME to avoid messing with my ârealâ jenny data.
I wrote some code to try out non-hash reply subjects formatted as (replyto ), while keeping the ability to use the existing hash style.
I donât think we need to decide all at once. If clients add support for a new method then people can use it if they like. The downside of course is that this costs developer time, so I decided to invest a few hours of my own time into a proof of concept.
With apologies to @movq@www.uninformativ.de for corrupting jennyâs beautiful code. I donât write this expecting you to incorporate the patch, because it does complicate things and might not be a direction you want to go in. But if you like any part of this approach feel free to use bits of it; I release the patch under jennyâs current LICENCE.
Supporting both kinds of reply in jenny was complicated because each email can only have one Message-Id, and because itâs possible the target twt will not be seen until after the twt referencing it. The following patch uses an sqlite database to keep track of known (url, timestamp) pairs, as well as a separate table of (url, timestamp) pairs that havenât been seen yet but are wanted. When one of those âwantedâ twts is finally seen, the mail file gets rewritten to include the appropriate In-Reply-To header.
Patch based on jenny commit 73a5ea81.
https://www.falsifian.org/a/oDtr/patch0.txt
Not implemented:
- Composing twts using the (replyto âŚ) format.
- Probably other important things Iâm forgetting.
@prologic@twtxt.net Wikipedia claims sha1 is vulnerable to a âchosen-prefix attackâ, which I gather means I can write any two twts I like, and then cause them to have the exact same sha1 hash by appending something. I guess a twt ending in random junk might look suspcious, but perhaps the junk could be worked into an image URL like
. If thatâs not possible now maybe it will be later.git only uses sha1 because theyâre stuck with it: migrating is very hard. There was an effort to move git to sha256 but I donât know its status. I think there is progress being made with Game Of Trees, a git clone that uses the same on-disk format.
I canât imagine any benefit to using sha1, except that maybe some very old software might support sha1 but not sha256.
@movq@www.uninformativ.de Agreed that hashes have a benefit. I came up with a similar example where when I twted about an 11-character hash collision. Perhaps hashes could be made optional somehow. Like, you could use the âreplytoâ idea and then additionally put a hash somewhere if you want to lock in which version of the twt you are replying to.
There is nothing wrong with how we currently run a diff to see what has been removed. if i build a merkle tree off all the twt hashes in a feed i can use that to verify a twt should be in a feed or not. and gossip that to my peers.
So.. basically a rehash of the email âunsendâ requests? What if i was to make a (delete: 5vbi2ea) .. would it delete someone elses twt?
@falsifian@www.falsifian.org âI donât really mind if the twt gets edited before I even fetch it.â, right, thatâs never the problem. Editing a twtxt before anyone fetches it isnât even editing, right? :-P The problem we are trying to fix is the havoc is causes editing twtxts that have already been replied to, often ad nauseam. Thatâs the real problem.
@quark@ferengi.one I donât really mind if the twt gets edited before I even fetch it. I think itâs the idea of my computer discarding old versions itâs fetched, especially if itâs shown them to me, that bugs me.
But I do like @movq@www.uninformativ.deâs suggestion on this thread that feeds could contain both the original and the edited twt. I guess it would be up to the author.
@quark@ferengi.one It looks like the part about traditional topics has been removed from that page. Here is an old version that mentions it: https://web.archive.org/web/20221211165458/https://dev.twtxt.net/doc/twtsubjectextension.html . Still, I donât see any description of what is actually allowed between the parentheses. May be worth noting that twtxt.net is displaying the twts with the subject stripped, so some piece of code is recognizing it as a subject (or, at least, something to be removed).
@falsifian@www.falsifian.org based on Twt Subject Extension, your subject is invalid. You can have custom subjects, that is, not a valid hash, but you simply canât put anything, and expect it to be treated as a TwtSubject, me thinks.
Hmm, but yarnd also isnât showing these twts as being part of a thread. @prologic@twtxt.net you said yarnd respects customs subjects. Shouldnât these twts count as having a custom subject, and get threaded together?
yarnd just doesnât render the subject. Fair enough. Itâs (replyto http://darch.dk/twtxt.txt 2024-09-15T12:50:17Z), and if you donât want to go on a hunt, the twt hash is weadxga: https://twtxt.net/twt/weadxga
@sorenpeter@darch.dk I like this idea. Just for fun, Iâm using a variant in this twt. (Also because Iâm curious how it non-hash subjects appear in jenny and yarn.)
URLs can contain commas so I suggest a different character to separate the url from the date. Is this twt Iâve used space (also after âreplytoâ, for symmetry).
I think this solves:
- Changing feed identities: although @mckinley@twtxt.net points out URLs can change, I think this syntax should be okay as long as the feed at that URL can be fetched, and as long as the current canonical URL for the feed lists this one as an alternate.
- editing, if you donât care about message integrity
- finding the root of a thread, if youâre not following the author
An optional hash could be added if message integrity is desired. (E.g. if you donât trust the feed author not to make a misleading edit.) Other recent suggestions about how to deal with edits and hashes might be applicable then.
People publishing multiple twts per second should include sub-second precision in their timestamps. As you suggested, the timestamp could just be copied verbatim.
Maybe Iâm being a bit too purist/minimalistic here. As I said before (in one of the 1372739 posts on this topic â or maybe I didnât even send that twt, I donât remember đ ), I never really liked hashes to begin with. They arenât super hard to implement but they are kind of against the beauty of the original twtxt â because you need special client support for them. Itâs not something that you could write manually in your
twtxt.txtfile. With @sorenpeter@darch.dkâs proposal, though, that would be possible.
Tangentially related, I was a bit disappointed to learn that the twt subject extension is now never used except with hashes. Manually-written subjects sounded so beautifully ad-hoc and organic as a way to disambiguate replies. Maybe Iâll try it some time just for fun.
@movq@www.uninformativ.de Iâm glad you like it. A mention (@<movq https://www.uninformativ.de/twtxt.txt>) is also long, but we live with it anyway. In a way a replyto: is just a mention of a twt instead of a feed/person. Maybe we chould even model the syntax for replies on mentions: (#<2024-09-17T08:39:18Z https://www.eksempel.dk/twtxt.txt>) ?!
@prologic@twtxt.net Brute force. I just hashed a bunch of versions of both tweets until I found a collision.
I mostly just wanted an excuse to write the program. I donât know how I feel about actually using super-long hashes; could make the twts annoying to read if you prefer to view them untransformed.
@prologic@twtxt.net earlier you suggested extending hashes to 11 characters, but hereâs an argument that they should be even longer than that.
Imagine I found this twt one day at https://example.com/twtxt.txt :
2024-09-14T22:00Z Useful backup command: rsync -a â$HOMEâ /mnt/backup
and I responded with â(#5dgoirqemeq) Thanks for the tip!â. Then Iâve endorsed the twt, but it could latter get changed to
2024-09-14T22:00Z Useful backup command: rm -rf /some_important_directory
which also has an 11-character base32 hash of 5dgoirqemeq. (Iâm using the existing hashing method with https://example.com/twtxt.txt as the feed url, but Iâm taking 11 characters instead of 7 from the end of the base32 encoding.)
Thatâs what I meant by âspoofingâ in an earlier twt.
I donât know if preventing this sort of attack should be a goal, but if it is, the number of bits in the hash should be at least two times log2(number of attempts we want to defend against), where the âtwo timesâ is because of the birthday paradox.
Side note: current hashes always end with âaâ or âqâ, which is a bit wasteful. Maybe we should take the first N characters of the base32 encoding instead of the last N.
Code I used for the above example: https://fossil.falsifian.org/misc/file?name=src/twt_collision/find_collision.c
I only needed to compute 43394987 hashes to find it.
HTTPS is supposed to do [verification] anyway.
TLS provides verification that nobody is tampering with or snooping on your connection to a server. It doesnât, for example, verify that a file downloaded from server A is from the same entity as the one from server B.
I was confused by this response for a while, but now I think I understand what youâre getting at. You are pointing out that with signed feeds, I can verify the authenticity of a feed without accessing the original server, whereas with HTTPS I canât verify a feed unless I download it myself from the origin server. Is that right?
I.e. if the HTTPS origin server is online and I donât mind taking the time and bandwidth to contact it, then perhaps signed feeds offer no advantage, but if the origin server might not be online, or I want to download a big archive of lots of feeds at once without contacting each server individually, then I need signed feeds.
feed locations [being] URLs gives some flexibility
It does give flexibility, but perhaps we should have made them URIs instead for even more flexibility. Then, you could use a tag URI,
urn:uuid:*, or a regular old URL if you wanted to. The spec seems to indicate that theurltag should be a working URL that clients can use to find a copy of the feed, optionally at multiple locations. Iâm not very familiar with IP{F,N}S but if it ensures you own an identifier forever and that identifier points to a current copy of your feed, it could be a great way to fix it on an individual basis without breaking any specs :)
Iâm also not very familiar with IPFS or IPNS.
I havenât been following the other twts about signatures carefully. I just hope whatever you smart people come up with will be backwards-compatible so it still works if Iâm too lazy to change how I publish my feed :-)
@sorenpeter@darch.dk There was a client that would generate a unique hash for each twt. It didnât get wide adoption.
@prologic@twtxt.net do that mean that for every new post (not replies) the client will have to generate a UUID or similar when posting and add that to to the twt?
@lyse@lyse.isobeef.org This looks like a nice way to do it.
Another thought: if clients canât agree on the url (for example, if we switch to this new way, but some old clients still do it the old way), that could be mitigated by computing many hashes for each twt: one for every url in the feed. So, if a feed has three URLs, every twt is associated with three hashes when it comes time to put threads together.
A client stills need to choose one url to use for the hash when composing a reply, but this might add some breathing room if thereâs a period when clients are doing different things.
(From what I understand of jenny, this would be difficult to implement there since each pseudo-email can only have one msgid to match to the in-reply-to headers. I donât know about other clients.)
@movq@www.uninformativ.de Another idea: just hash the feed url and time, without the message content. And donât twt more than once per second.
Maybe you could even just use the time, and rely on @-mentions to disambiguate. Not sure how that would work out.
Though I kind of like the idea of twts being immutable. At least, itâs clear which version of a twt youâre replying to (assuming nobody is engineering hash collisions).
@prologic@twtxt.net Some criticisms and a possible alternative direction:
Key rotation. Iâm not a security person, but my understanding is that itâs good to be able to give keys an expiry date and replace them with new ones periodically.
It makes maintaining a feed more complicated. Now instead of just needing to put a file on a web server (and scan the logs for user agents) I also need to do this. What brought me to twtxt was its radical simplicity.
Instead, maybe we should think about a way to allow old urls to be rotated out? Like, my metadata could somehow say that X used to be my primary URL, but going forward from date D onward my primary url is Y. (Or, if you really want to use public key cryptography, maybe something similar could be used for key rotation there.)
Itâs nice that your scheme would add a way to verify the twts you download, but https is supposed to do that anyway. If you donât trust https to do that (maybe you donât like relying on root CAs?) then maybe your preferred solution should be reflected by your primary feed url. E.g. if you prefer the security offered by IPFS, then maybe an IPNS url would do the trick. The fact that feed locations are URLs gives some flexibility. (But then rotation is still an issue, if I understand ipns right.)
@movq@www.uninformativ.de @prologic@twtxt.net Another option would be: when you edit a twt, prefix the new one with (#[old hash]) and some indication that itâs an edited version of the original tweet with that hash. E.g. if the hash used to be abcd123, the new version should start â(#abcd123) (redit)â.
What I like about this is that clients that donât know this convention will still stick it in the same thread. And I feel itâs in the spirit of the old pre-hash (subject) convention, though thatâs before my time.
I guess it may not work when the edited twt itself is a reply, and there are replies to it. Maybe that could be solved by letting twts have more than one (subject) prefix.
But the great thing about the current system is that nobody can spoof message IDs.
I donât think twtxt hashes are long enough to prevent spoofing.
@prologic@twtxt.net Perfect, thanks. For my own future reference: curl -H âAccept: application/jsonâ https://twtxt.net/twt/st3wsda
@prologic@twtxt.net Specifically, I could view yarndâs copy here, but only as rendered for a human to view: https://twtxt.net/twt/st3wsda
@movq@www.uninformativ.de thanks for getting to the bottom of it. @prologic@twtxt.net is there a way to view yarndâs copy of the raw twt? The edit didnât result in a visible change; being able to see what yarnd originally downloaded would have helped me debug.
@prologic@twtxt.net One of your twts begins with (#st3wsda): https://twtxt.net/twt/bot5z4q
Based on the twtxt.net web UI, it seems to be in reply to a twt by @cuaxolotl@sunshinegardens.org which begins âIâve been sketching outâŚâ.
But jenny thinks the hash of that twt is 6mdqxrq. At least, thereâs a very twt in their feed with that hash that has the same text as appears on yarn.social (except with â instead of â).
Based on this, it appears jenny and yarnd disagree about the hash of the twt, or perhaps the twt was edited (though I canât see any difference, assuming â vs â is just a rendering choice).
@prologic@twtxt.net I believe you when you say registries as designed today do not crawl. But when I first read the spec, it conjured in my mind a search engine. Now I donât know how things work out in practice, but just based on reading, I donât see why it canât be an API for a crawling search engine. (In fact I donât see anything in the spec indicating registry servers shouldnât crawl.)
(I also noticed that https://twtxt.readthedocs.io/en/latest/user/registry.html recommends âThe registries should sync each others user list by using the users endpointâ. If I understood that right, registering with one should be enough to appear on others, even if they donât crawl.)
Does yarnd provide an API for finding twts? Is it similar?
I just manually followed the steps at https://dev.twtxt.net/doc/twthashextension.html and got 6mdqxrq. I wonder what happened. Did @cuaxolo@sunshinegardens.org edit the twt in some subtle way after twtxt.net downloaded it? I couldnât spot a diff, other than â appearing as â on yarn.social, which I assume is a transformation done by twtxt.net.
@prologic@twtxt.net How does yarn.socialâs API fix the problem of centralization? I still need to know whose API to use.
Say I see a twt beginning (#hash) and I want to look up the start of the thread. Is the idea that if that twt is hosted by a a yarn.social pod, it is likely to know the thread start, so I should query that particular pod for the hash? But what if no yarn.social pods are involved?
The community seems small enough that a registry server should be able to keep up, and I can have a couple of others as backups. Or I could crawl the list of feeds followed by whoever emitted the twt that prompted my query.
I have successfully used registry servers a little bit, e.g. to find a feed that mentioned a tag I was interested in. Was even thinking of making my own, if I get bored of my too many other projects :-)
@movq@www.uninformativ.de Thanks, it works!
But when I tried it out on a twt from @prologic@twtxt.net, I discovered jenny and yarn.social seem to disagree about the hash of this twt: https://twtxt.net/twt/st3wsda . jenny assigned it a hash of 6mdqxrq but the URL and prologicâs reply suggest yarn.social thinks the hash is st3wsda. (And as a result, jenny âfetch-context didnât work on prologicâs twt.)
@movq@www.uninformativ.de I think you are worrying about a non-issue. I see nothing to do on your example twt, because there is no context. Furthermore, if I wanted to follow the feed, everything I need is already on that twt example. :-)
@prologic@twtxt.net Yes, fetching the twt by hash from some service could be a good alternative, in case the twt I have does not @-mention the source. (Besides yarnd, maybe this should be part of the registry API? I donât see fetch-by-hash in the registry API docs.)
@movq@www.uninformativ.de I donât know if Iâd want to discard the twts. I think what Iâm looking for is a command âjenny -g https://host.org/twtxt.txtâ to fetch just that one feed, even if itâs not in my follow list. I could wrap that in a shell script so that when I see a twt in reply to a feed I donât follow, I can just tap a key and the feed will get added to my maildir. I guess the script would look for a mention at the start of a selected twt and call jenny -g on the feed.
@movq@www.uninformativ.de Is there a good way to get jenny to do a one-off fetch of a feed, for when you want to fill in missing parts of a thread? I just added @slashdot@feeds.twtxt.net to my private follow file just because @prologic@twtxt.net keeps responding to the feed :-P and I want to know what heâs commenting on even though I donât want to see every new slashdot twt.
twts are taking a very long time to post from yarn after the latest upgrade. Like a good 60 seconds.
@prologic@twtxt.net I donât know if this is new, but Iâm seeing:
Jul 25 16:01:17 buc yarnd[1921547]: time="2024-07-25T16:01:17Z" level=error msg="https://yarn.stigatle.no/user/stigatle/twtxt.txt: client.Do fail: Get \"https://yarn.stigatle.no/user/stigatle/twtxt.txt\": dial tcp 185.97.32.18:443: i/o timeout (Client.Timeout exceeded while awaiting headers)" error="Get \"https://yarn.stigatle.no/user/stigatle/twtxt.txt\": dial tcp 185.97.32.18:443: i/o timeout (Client.Timeout exceeded while awaiting headers)"
I no longer see twts from @stigatle@yarn.stigatle.no at all.
This is a test. I am not seeing twts from @stigatle@yarn.stigatle.no and it seems like @prologic@twtxt.net might not be seeing twts from me. Do people see this?
@prologic@twtxt.net I am not seeing twts from @stigatle@yarn.stigatle.no anymore. Are you seeing twts from me?