@lyse@lyse.isobeef.org But I wannaaaaaaaaaaaaaaaaaaa. š
Letās see how this goes: https://bugzilla.mozilla.org/show_bug.cgi?id=2065883
@brytboi@twtpub.com I hope youāre seeing my replies because you absolutely can scroll up in the app. Let me know if youāve run into a bug though and report it to me so I can fix it immediately!
@david@daiwei.me Are you able to describe this bug as an Issue in the Gitea Issue Tracker such that it can be reproduced and fixed? š
Data protection, @dce@hashnix.club! :-D
This revealed another bug in my client that I still need to fix at some point. Inserting an empty set of messages failed with an SQL logic error. Whoops. I didnāt think about that corner case.
jenny stuff aside, I received zero bug reports or code contributions since leaving GitHub in 2018.
@movq@www.uninformativ.de Finally, your software is just perfect and finished by now, no need to report non-existing bugs or send in code changes. :-) How many tickets and merge requests did you get before moving to your own server?
I have to admit that I use git format-patch so rarely, I always have to pull it up from my shell history. Havenāt used git send-email even once. I definitely have to look into that soon. Wanted to do that for several years. I typically upload the patch to my server and send a link via IRC.
Maybe I was just very unlucky, but my experience is that you can perfectly ignore people and their work who only do it for the āfameā. Itās almost always been from inferior quality to say the least.
@prologic@twtxt.net Hmmm, I have no idea how to solve that problem. š
Some jenny stuff aside, I received zero bug reports or code contributions since leaving GitHub in 2018.
I thought I had made that super easy, because you can just send me an email ā no sign-up process, nothing. But thatās way too old-school, people donāt know how to use git format-patch (let alone git send-email) and they also donāt understand that they can just send me a link to their forked Git repo (which can be hosted anywhere). Git is super flexible and powerful, but those features are hardly ever used.
Maybe people even need some kind ārewardā, or āfameā. Like those āachievementsā that you can unlock on GitHub. (Something to put in their CV ⦠?)
Okay, so, my website also includes my code / git repos, and those are made browsable by stagit. What I donāt like about this (these days) is that this includes all the diffs of my commits. In other words: All my code.
This makes it super easy for malicious crawlers to slurp up valuable data. I donāt like that.
Iām thinking about switching to this instead:
It still shows project infos and there are Atom feeds, but to get the code, you have to actually clone the repos.
š¤
(If you spot any bugs, let me know.)
tt. I run into this bug almost daily for weeks now.
@lyse@lyse.isobeef.org Ahh ok! I was just wondering and curious whether it was a bug that Iāve caused anywhere along the way š§
tt. I run into this bug almost daily for weeks now.
@prologic@twtxt.net A screenshot wonāt help in this case, as you donāt see anything. :-D It starts off just fine with a conversation tree like that:
Unknown conversation root
āā“Read reply
āā“Read subreply
Everything works. After reloading the feeds, a new message becomes part of the conversation, so the conversation e.g. looks:
Unknown conversation root
āā“Read reply
āā“Read subreply
āā“Unread subreply
However, the bug is that the whole conversation is not shown at all. None of the three (or four with the root) messages appear in the message tree view. My recursive SQL determining the messages to display is clearly broken.
Ahh yes! Please do upgrade your twtd instance. Few things changed, many bugs fixed there too.
I should really fix this damn bug where new replies to read replies to unknown conversation roots are not showing up in tt. I run into this bug almost daily for weeks now.
@dce@hashnix.club Fixed! š„³ That 403 was our bug ā connect was checking your token via /api/v1/user (needs read:user), but your tokenās scoped to just the repo so it canāt. Now it validates against the repo itself instead š
@david@daiwei.me I think it might be a bug i just fixed š¤
twtxt.net senders š¤¦āāļø). Fixed + deployed now š„³ give the hosted feed another go, it'll land this time š¤
@david@daiwei.me Found it. Some bugs in the āclaim limiterā. Fixingā¦
based on this, itās entirely possible that there may still be a subtle bug somewhere with the app
@movq@www.uninformativ.de please donāt waste your time to bugging this. Iāll figure out whatās going on with these new clients.š
Hurray, I can now press gg instead of g to go to the top in tt. Much better! :-) Other multi-key combinations are also easily possible now.
I should probably write a real article about this at some point, but here we go. The only downside with my new key binding system is that it breaks tviewās established pattern. Youāve got an InputHandler(), that is implemented using WrapInputHandler(ā¦). It typically then directly implements the switching logic depending on the key press. Something like this:
func (w *Widget) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
// WrapInputHandler allows for intercepting key events with SetInputCapture(ā¦)
// from the outside for customization. This handles the default key bindings.
return t.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
switch event.Key() {
case tcell.KeyRune:
if event.Modifiers() == tcell.ModNone {
switch event.Rune() {
case 'k':
w.scrollUp()
return // we already handled the event, stop processing
case 'j':
w.scrollDown()
return
}
}
}
// We didn't handle the key event. Maybe the parent
// widget knows what to do with it.
if handler := w.parent.InputHandler(); handler != nil {
handler(event, setFocus)
}
})
}
From the outside, you can intercept and either stop or continue the widgetās original key handling with a potentially rewritten key event using SetInputCapture(ā¦):
w := NewWidget()
// customized or additional key bindings
w.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
switch event.Key() {
case tcell.KeyUp:
// Rewrite the event, so the "cursor up" key is an alias
// for the vim key binding "k", that is handled by the
// wrapped input handler above. (I know, I know, this is a
// completely unrealistic example, why would anyone use
// cursor keys when there are vim key bindings available?!)
return tcell.NewEventKey(tcell.KeyRune, 'k', tcell.ModNone)
case tcell.KeyRune:
if event.Modifiers() == tcell.ModNone {
switch event.Rune() {
case 'q':
app.Stop()
// we already handled the event, do not pass it
// to the wrapped input handler above
return nil
case 'r':
toggleMessageReadStatus()
return nil
}
}
}
// we didn't handle the event, pass it to the wrapped
// input handler above
return event
}
Since they all expect a single key, Iāve noticed that using multiple dedicated KeyBindings of mine on these different levels kinda breaks multi-key handling with common prefixes. The outer-most KeyBinding captures the prefix, but it canāt transfer it to the inner one if not handled by the outer one. At least not without some more (potentially ugly) changes. So, I now have to work with just a single KeyBindings object for the entire widget chain (if it consists of multiple other widgets or the regular input handler and input capture are in the game). The outside needs to register all its key bind customizations or extensions at the same level that the original widget handles its default ones. Doable by exposing the widgetās KeyBindings instance, but not pretty. You always have to keep this in mind.
With the KeyBindings, it will look like that:
type Widget struct {
parent tview.Primitive
// make it available to children or the outside either by
// direct field access or by providing a getter method
KeyBindings *bind.KeyBindings
}
func NewWidget() *Widget {
w := &Widget{KeyBindings: &bind.KeyBindings{}}
w.KeyBindings. // default key bindings
Bind0(bind.KeySequence('k', w.scrollUp).
Bind0(bind.KeySequence('j', w.scrollDown)
return w
}
func (w *Widget) InputHandler() InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
return t.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
// also note the missing support for focus transfer at the moment
event = w.KeyBindings.Capture(event)
if event == nil {
return
}
if handler := w.parent.InputHandler(); handler != nil {
handler(event, setFocus)
}
}
}
And then from the outside, or in a child widget:
w := NewWidget()
w.KeyBindings. // additional or customized key bindings
Bind1(bind.KeySequence(tcell.KeyUp), func(*tcell.EventKey) *tcell.EventKey {
return tcell.NewEventKey(tcell.KeyRune, 'k', tcell.ModNone)
}).
Bind0(bind.KeySequence('q'), app.Stop).
Bind0(bind.KeySequence('r'), toggleMessageReadStatus)
When directly working with tview primitives that are not part of custom widget implementations, the following works well so far:
textView := tview.NewTextView().
SetWordWrap(true).
SetText("ā¦")
SetScrollable(true)
textView.SetInputCapture((&bind.KeyBindings{}).
Bind0(bind.KeySequence('q'), app.Stop).
Bind1(bind.KeySequence('g', 'g'), func(*tcell.EventKey) *tcell.EventKey {
return tcell.NewEventKey(tcell.KeyHome, 0, tcell.ModNone)
}).
Capture)
I need to sleep on this some more.
Also, writing very long messages like this one is really not all that fun in ttās editor. I should absolutely provide a way to shell out to vim.
(Took me about one and a half hours to compose, holy crap. But not only because of not using vim. Although, that might have saved me a quarter hour or so for sure. Proof-reading this message also uncovered quite a few bugs in my real documentation. So, thatās a big win!) Good night!
I believe weāve nailed all the bugs down𤣠Though i am sick at the moment so iām not at my best š¢
@balloon-fu-sen@tw.fus.f5.si Ahhh! Thatās a bug! Lemme fix that!
š„³ Finally! After nearly 4 years, yarnd v0.16.0 āSilver Sojournerā is out! š Twt Hash v2, SQLite FTS5 search, HTMX-powered UI, first-time setup wizard and literally hundreds of bug fixes š
Release notes: https://git.mills.io/yarnsocial/yarn/releases/tag/0.16.0
Upgrading is fully automatic ā the Twt Hash v2 migration re-fetches all feeds on first start, so expect the first cycle to be a bit heavier. Images on Docker Hub as prologic/yarnd:0.16.0 š
cc @kat@yarn.girlonthemoon.xyz @abucci@anthony.buc.ci @shinyoukai@yume.laidback.moe @eldersnake@we.loveprivacy.club š
@bender@twtxt.net No idea. I can only tell you that the correct hash would have been rwzz277nkyju for this line:
[2026-07-11 14:47:17+00:00] [(#5bpwpdcjnhcz) <a href="https://yarn.girlonthemoon.xyz/external?uri=https://daiwei.me/twtxt.txt">@david<em>@daiwei.me</em></a> (This thread is broken again on my end. Another bug or fix not released yet? š
)]
@david@daiwei.me (This thread is broken again on my end. Another bug or fix not released yet? š )
I will aim to have most issues bugs and user experience problems, identified and fixed by this weekend!
And if we can compile a list and file issues for feeds, twtxt.app and anything else as issues for when i get back š feature requests, bug reports. etc š¤
FYi š Iām aware of an optimist precomputed hashing bug on the new twtxt.app 𤯠Trying to work with @bender@twtxt.net remotely on my vacation yo fix it š¤£
@movq@www.uninformativ.de I figure there is a bug somewhere, but where?

Whatās the bug?
@movq@www.uninformativ.de we found a bug. @prologic@twtxt.net loves it!
lleeypvkzbw2? That Twt was never ingested by twtxt.net (and likely the search engine) so umm hmmm threading breaks š¤£
I think I fixed this bug!
@lyse@lyse.isobeef.org Found it and fixed it! š The crawlerās discovery spider was fetching every feed a second time, without any conditional headers (plus a couple of other politeness bugs: redirected feed URLs never stored their cache validators, and there was no floor between re-fetches). Now every feed is fetched at most once per crawl, always with If-Modified-Since / If-None-Match, and never more than once per 15m no matter what. Just deployed ā please keep an eye on your access logs and let me know if you still see anything impolite from the crawler š
@lyse@lyse.isobeef.org Thanks! Iāll look into that! Could be a bug in the crawler.
Hey folks š Today I announce the re-release of the Twtxt Search Engine now live and running and actively re-crawling and re-indexing. š Please report bugs or any useability issues to me! š #Twtxt #Search
My mate and I hiked up the backyard mountain. We got 25°C and quite some wind, so it was actually not too terrible. The wind could have blown harder or the temps a little lower, but oh well.
I saw the squirrelās bushy tail stick up on the forest floor in the sunlight and immediately thought of this cute little feller. Since it didnāt move at all, even when we came closer, I got irritated and reconsidered that it might actually be some kind of dried up farn. But then we also were able to see its body. Unfortunately, the squirrel ran up the tree too quickly, so all the shots are kinda crap.
At one flower spot, there were sooo many butterflies, wasps, flies, bugs and other insects. The botanic was completely crowded.
The workers were transferring logs from one log truck to the other in a parking lot. Iāve never seen this happening before. When we passed the same place on the way home, they had moved logs into a sea container. That was surprising. This semi wasnāt there on the way there. One log was probably too long and sticking out the container, so they probably had to wait for somebody to return with a chainsaw. Crazy that theyāre shipping logs from here probably overseas. Why else would they put them in a sea container?
After our first break, a blackbird was really posing for us with his worm in the dark shade.
Today was my first time I ever saw a hummingbird hawk-moth (TaubenschwƤnzchen) for real. My mate photographed them many, many times before, but I never came across one myself. So, that was really special.
The forest service installed an outdoor table with two benches next to the timber lion, that was cool to see. We sat down for a few minutes and enjoyed both the view into the Fils valley and ant on the tabletop, but the sun was beating down too heavily on us, so we had to move on.
All in all, it was a very nice few hours long hike. Enjoy! https://lyse.isobeef.org/waldspaziergang-2026-07-03/
@lyse@lyse.isobeef.org oh yes! And, when I mow the lawn (which reminds me I need to mow the front soonish), you can add dust, bugs, and grass blades to the equation. Just ālovelyā. š
@lyse@lyse.isobeef.org If I were to guess: They might have done so to avoid bug reports from users with heavily outdated versions. š«¤
there was supposed to be a plus in there but it got eaten by a bug!
@lyse@lyse.isobeef.org Interesting approach. š¤
The master branch should never be in a broken state (apart from bugs I donāt know about). Any intermediate state during the development of a larger feature will happen in a different branch.
I mean, yeah, but ⦠I donāt know, I like having ātraditional releasesā as a second safety net when I write programs. I like to let things mature for a while and then I cut a new release. So itās, like, āwe have a bunch of new features and fixes here, and to the best of my knowledge this works fine nowā. But maybe Iām just paranoid. š¤
@movq@www.uninformativ.de Oh yeah, way better! :-) I didnāt spot the bug, though.
I think I could work with the feature set. I typically donāt need a lot. Until I do. :-D The message tree in tt is an example of that. But tt is also special that it needs something like this in the first place. Itās unusual.
(And of course thereās a bug because Iām an idiot. š¤Ŗ)
@itsericwoodward@itsericwoodward.com Turns out, this is a bug in my config to cache synchronization. Nickname changes in the configuration file are just not synced to the cache at startup if the feed URL already exists in the cache. I must have fixed this typo in my config ages ago, because I donāt even recall having that spelling mistake to begin with. Yet, the cache was happily showing the erroneous nickname. Composing a reply automatically adds the mentions from the conversation participants. Everything originates from the cache, so, I successfully poissoned my replies.
@movq@www.uninformativ.de Honestly I think you build the team before you need the PRs š¤ Start with relationships ā people whoāve been using your software, filing good bug reports, asking smart questions. Those are your future maintainers. The PR comes later as a formality, not a tryout š
(#vixabsa) @movq@www.uninformativ.de Honestly I think you build the team before you need the PRs š¤ Start with relationships ā people whoāve been using your software, filing good bug reports, asking smart questions. Those are your future maintainers. The PR comes later as a formality, not a tryout š
Yay finally fixed some of those annoying āMark as Readā behaviours/bugs š
<updated> of the feed, too. But for some reason, some articles were suddenly marked as new.
I wasted my entire weekend on the writeup. If you have way too much time to spare and also are interested in a bug analysis of a software that you donāt even use, I have you covered: https://lyse.isobeef.org/newsboat-time-parsing-bug-analysis/
Oh boy, it was bloody humid this morning. Just around 20°C when we left, but climbing rapidly. The flow of air when walking was okay, but as soon as we stopped, streams of sweat were pouring down on us. Luckily, it was cloudy, but the lack of wind was bad. Now, the sun is out, 29°C will be reached in an hour and Iām glad that the house is still cool. It will be a different story in a few weeks or months. Not looking forward to that at ll.
On the bright side, we saw the first tadpoles of the year and an also first, but sadly dead slow worm that probably some bird dropped on a bench next to the fountain. The fly was stuck to its feast and also cactus. The municipality fixed the railing nicely and we came across a giant patch of great looking fire bugs on the summit.
All in all, a successful stroll through the woods but for the humid heat.
@prologic@twtxt.net Ahh, I see. Okay, Iām with you there. On this high level, I can understand how the thing works.
Maybe my wording isnāt good. š¤ Letās take a real life example from what we do at work.
Thereās this AI chatbot. It gets support requests from users, so the user says something like āI need access to a particular systemā. This triggers the bot to ārunā the instructions stored in a large Markdown file, like ācheck if the user is authorized to do this, then issue the following API requestsā, and so on. This is essentially like running a little script, except itās written in natural language (German) and thereās no āscript interpreterā but just the AI.
Now, suppose that the AI doesnāt quite do what was intended. Thereās some subtle bug. How do you debug this? How do you find out how the AI came to the āconclusionā to run step A instead of step B? And how do you find out how exactly you have to change your prompt so this doesnāt happen again next time?
If this was an actual script/program instead of AI, you could repeat the request and attach a debugger or throw in some printf() or whatever. How do you do that kind of thing with AI? How do you pinpoint exactly what the problem was?
(Or is this just a stupid idea? Do we have to give up that way of thinking when using AI? Is the era of debuggability over?)
<updated> of the feed, too. But for some reason, some articles were suddenly marked as new.
Aha, yesterdayās newly added support for LC_TIME to render localized timestamps also broke the feed parsing with my LANG=de_DE.UTF-8 and LC_CTYPE=de_DE.UTF-8 environment. :-)
Atom feeds make use of RFC 3339 timestamps. They are first converted into RFC 882 timestamp representation, which is the one that RSS feeds use. However, this conversion now results in localized RFC 882 timestamps, which cannot be parsed into Unix timestamp numbers via curl_getdate(ā¦). I bet that it doesnāt know about the localization at all and expects English month and weekday names. Looking at its docs, I reckon that function was selected because of its myriad of supported timestamp formats: https://curl.se/libcurl/c/curl_getdate.html RFC 3339 is not included, though, hence the transformation up front.
The intermediate Item objects in the parser domain use std::string for the timestamp representation. This isnāt all that silly, because Newsboat supports all sorts of different feed formats with different timestamp formats. These RFC 883 timestamps are centrally parsed into time_t.
Speaking of time: Itās time to go to bed after this late bug hunting fun. :-)
You didnāt change your Atom feed by any chance yesterday or today, @movq@www.uninformativ.de? Not only do I have a metric shitton of ānewā old items in my YouTube feeds, but also a bunch of your old articles are shown as new.
I fear that this is a Newsboat bug. I rebuilt it yesterday from master.
Eehhh, what the hell is going on here!?
SELECT
printf("0x%x", (1 << 63) - 2),
printf("0x%x", (1 << 63) - 1),
printf("0x%x", 1 << 63 ),
printf("0x%x", (1 << 63) + 1),
printf("0x%x", (1 << 63) + 2)
SQLite yields:
0x8000000000000000 (instead of 0x7ffffffffffffffe)
0x8000000000000000 (instead of 0x7fffffffffffffff)
0x8000000000000000 (correct)
0x8000000000000001 (correct)
0x8000000000000002 (correct)
Huh!? O_o Am I stupid? What am I missing here? Or is this actually a bug? :-?
With 62 bits, everything is spot on:
0x3ffffffffffffffe
0x3fffffffffffffff
0x4000000000000000
0x4000000000000001
0x4000000000000002
And 64 bits rather unsurprisingly also yield:
0xfffffffffffffffe
0xffffffffffffffff
0x0
0x1
0x2
What do the Gopher Troopers think of the following? The Gopher protocol is a nearly-forgotten network protocol from the early 1990s, designed to serve and navigate text-based menus and documents over the Internet. While itĀs far less common than HTTP/HTTPS today, there are still some security risks associated with Gopher and Gopher space. LetĀs break them down carefully: 1. Lack of Encryption Problem: Gopher was designed long before widespread use of SSL/TLS. All dataĀincluding credentials, file transfers, and menu selectionsĀis transmitted in plaintext. Impact: Anyone intercepting traffic (e.g., via a network sniffer, public Wi-Fi, or a compromised router) can read sensitive information, including usernames and passwords. 2. No Authentication or Access Control Problem: Gopher servers rarely implement robust authentication; access control is usually limited or non-existent. Impact: Unauthorized users might browse sensitive directories or download private files, particularly if servers are misconfigured. 3. Server Software Vulnerabilities Problem: Modern OSes can still run legacy Gopher servers, but the software is often unmaintained. Impact: Old software may contain buffer overflows, directory traversal bugs, or command injection vulnerabilities that attackers could exploit. 4. Malicious Gopher Links Problem: Gopher menus can contain links that point to scripts or other servers, similar to hyperlinks in HTTP. A client following a malicious link could inadvertently: Download malware Access sensitive internal network resources (server-side request forgery) Impact: Could serve as a vector for attacks if a user opens content from untrusted sources. 5. Legacy Protocol Weaknesses Problem: Gopher lacks modern web security mechanisms like: Content security policies Same-origin policies Cross-site request forgery protection Impact: If Gopher is bridged to other services (like modern browsers via gateways), old vulnerabilities may be exposed. 6. Information Leakage Problem: Gopher servers often provide directory listings without restriction. Impact: Sensitive files, backup directories, and internal documents may be exposed unintentionally. 7. Bridging Risks Problem: Some modern browsers access Gopher via gateways (HTTP-to-Gopher proxies). These bridges may: Expose sensitive internal resources to the gateway Introduce logging or tracking that wouldnĀt exist on pure Gopher Impact: Attacks could occur indirectly through insecure intermediaries. Key Takeaways Gopher is inherently insecure due to its design in a pre-HTTPS era. Main threats: eavesdropping, unauthorized access, malware delivery, and exploitation of unpatched server software. Safe practice: Use Gopher only in isolated, trusted environments, or through secure HTTP(S) gateways with proper sanitization.