
Onchain Zaps in Ditto: Bitcoin Tipping Without Lightning
Alex Gleason, 6/12/2026, 5 engagements
I create software that empowers people online. I'm vegan btw.
Filtering by: ditto ยท clear filter

Alex Gleason, 6/12/2026, 5 engagements

Alex Gleason, 7/15/2024

Alex Gleason, 6/14/2024
Another #ditto itch scratched: enable "reactions" and "zaps" in your home feed settings to see your home feed go crazy. Spoiler: nobody zaps each other.



Any follow list (kind 3), follow pack, or people set can now also act as a shared mute list in #ditto



#Ditto now publishes a NIP-89 application handler event and displays "client" tags on posts.


This Sprint Planner for #Ditto managed by
is one of the best I've worked with.
It's one giant vector graphic instead of a soul crushing app like Jira.


#Ditto's domain: search now indexes NIP-05 hostnames separately from domains, so domain:mostr.pub will show all subdomains of the Mostr bridge, while domain:mastodon-social.mostr.pub will only show mastodon.social posts.
You can even combine multiple domains in searches with other tokens, eg:
domain:nostrplebs.com domain:primal.net reply:false
#Ditto now supports negated search terms!
Want to see content about Bitcoin but not Jesus? Try:
bitcoin -jesus
#Ditto now displays daily streaks! To have a streak, no more than 24 hours can pass since your last post.


Language detection is surprisingly difficult. The neural networks get basic things wrong. They will say that Korean text is actually Chinese, even though you can obviously see with your eyes that it's not.
After multiple libraries failed this basic test, I did something brave and implemented a naive regex solution in #Ditto. It does a first pass on the text before moving it on to the neural network.
์๋ ํ์ธ์
For example, if ALL the characters are in Korean script, it must be Korean. Even if it's a nonsensical sequence of Korean characters, it cannot be any language other than Korean due to the fact Korean makes exclusive use of this character set.
There are only a few languages where this is possible: Korean, Greek, and Hebrew.
Again, this is only possible if ALL characters in the text match a target language, so simply using "ฯ" in a text does not make it Greek. So, currently this check is very narrow.
Notes about other languages:
Chinese: it's not possible to do a regex-only solution for Chinese, since Han script is also part of Japanese.
Japanese: we can definitively detect Japanese, as long as the text contains at least one Hirigana or Katakana character in addition to 0 or more Han characters. So at least some Japanese text can be unambiguously detected just by a regex.
Russian: Cyrillic text is used by a handful of languages besides Russian. BUT, if the text is entirely Cyrillic, that at least narrows down the possible languages it could be.
Next steps:
To optimize this, the regexes will narrow down possible languages of a text before passing it to the neural network.
For example, if a text is entirely Han, we would restrict the model to deciding only between Chinese and Japanese. If it's Cyrillic, we'd do the same thing, but with the 6 or so Cyrillic languages.
We could also try to match, say, 90% of the text instead of 100%, to any specific script, to catch outliers like occasional English words used in Japanese, etc. We are already stripping things like punctuation, emojis, and URLs before passing text to the model.
Finally, this is all so we can use a lightweight, embedded solution for language detection, instead of calling out to some proprietary API, or even a giant self-hosted solution. In that case, I believe a layered solution will always be needed. We have to do these naive checks to put "guardrails" on the model, so its guesses can't stray outside of common sense. Switching the model can improve it, but these naive checks will still be true.


#Ditto search now supports negative search tokens. That means you can use -protocol:activitypub to remove everything from the bridge, or even -language:de to remove the Germans.


Want to see only posts in Chinese from the Fediverse? This is now possible in #Ditto


and
are working on integrating Cashu wallets/nutzaps into #Ditto ๐


This is so fucking epic. #Ditto with JSON logging to Loki and Grafana. I can get a list of all SQL queries from all Ditto servers I run in the last 30 minutes that took > 1 second, complete with parameters.


I want to tell you why #Ditto was slow and how we fixed it.
There were 2 issues: Postgres performance and nip05 lookups.
Postgres performance was caused by profile lookups, eg:
{ kinds: [0], authors: [a, b, c, d, ...] }
The issue was ORDER BY clauses on those queries. This caused Postgres to do a "sequence scan" instead of using the correct index:
"nostr_events_replaceable_idx" UNIQUE, btree (kind, pubkey) WHERE kind >= 10000 AND kind < 20000 OR (kind = ANY (ARRAY[0, 3]))
I actually fixed this already last year, and then reverted it in the last few months due to a bad decision. ๐คฆ See: https://gitlab.com/soapbox-pub/nostrify/-/commit/6a51c3f8a73c8a9f852a46d57119afdd4bc25b53 and https://habla.news/u/[email protected]/nostr-filters-limit
Logging query errors exposed this. This is why logging matters, people! Now I am switching Ditto to use JSON logging and incorporating Loki + Promtail so can easily track these problems in the future.
As for nip05 lookups - Ditto actually does nip05 verification in realtime as feeds are being rendered. This is obviously very bad. It does have an in-memory LRU cache, but restarting Ditto resets it. As a result, feeds can take up to 3 seconds to load (that's the timeout I set for fetching nip05). And if fetching fails, usernames are not displayed correctly.
To solve this (with a bandaid solution), I configured squid (HTTP proxy) to aggressively cache nip05 responses with this:
refresh_pattern /.well-known/nostr.json 3600 0% 259200
I also had to configure Ditto and squid with self-signed SSL certs so it can mitm and actually cache:
http_proxy="http://squid.lan:3128" https_proxy="http://squid.lan:3128" DENO_CERT="/etc/ssl/certs/squid.crt"
My TODO is to cache nip05 verifications in Postgres, to solve the root problem for self-hosters and increase performance even more.
tl;dr Ditto is fast now.
I want Nostr to be the epicenter of the decentralized web.
Check out
's new designs to incorporate bridges directly into #Ditto ๐
More coming soon!



Kind 20 events now supported in #Ditto!
https://gitlab.com/soapbox-pub/ditto/-/merge_requests/615
Loading quoted postโฆ
Custom fields have been added to #Ditto! Merged, deployed, etc. Try it out on ditto.pub.
Code: https://gitlab.com/soapbox-pub/ditto/-/merge_requests/591
Loading quoted postโฆ



11/20/2024, 13 engagements
#Mostr Bridge got an upgrade. ๐
Now NIP-05 names on the bridge have a separate subdomain for each ActivityPub server.
For example: [email protected] has become [email protected]
This means that from Nostr, each ActivityPub server can be treated separately, for indexing and moderation purposes.
On #Ditto you can now look at a feed each ActivityPub server separately. And they even display a favicon from the upstream server!
Code: https://gitlab.com/soapbox-pub/mostr/-/merge_requests/123


11/11/2024, 13 engagements
Good evening witches and wizards!
I would like to tell you the story of the spookiest bug #Ditto has ever seen. And I'm happy to announce that on this All Hallows Eve,
and I worked together to make the spectral ghost visible and vanquish it! ๐ป
The story begins with
(Verita84), who faced this problem so bad he actually left Nostr and went back to the Fediverse.
The problem being: that no matter how hard he tried, he could not log in. He tried other browsers, deleted all data, turned his computer off and back on again. But although it worked perfectly 100% of the time on his other device, it worked 0% of the time on this device.
As the months went on, we received a few complaints here and there from people saying they could NEVER log in to Ditto on a particular device. But our team could never reproduce this problem. So it eluded us. But we would occasionally still hear rare complaints of it.
Well today was experiencing the exact same issue. So I added more logs, and he sent screenshots and videos, and we collaborated for several hours. I carefully combed through every line of code in the whole flow, and absolutely nothing was wrong!
Finally I thought to find the NIP-46 event in the Ditto logs, and it occurred to me to check its timestamp. Because there is a 10 second limit on these events, after which the relay would silently reject them.
Strangely, the event's created_at timestamp was actually 12 seconds before the Ditto log timestamp... Hmm...
I assumed he must have a very slow computer, or network connection. But he assured me it was very fast. I raised the limit from 10 seconds to 1 minute anyway. And you know what? IT FUCKING WORKED.
(I also added special treatment for ephemeral events in Ditto, so they would throw an error if they didn't get streamed out, instead of just absorbing it.)
But I told him, since his network was so slow, he would have to wait 12 seconds to log in... But to my surprise, he told me he didn't wait 12 seconds at all. It was instantaneous.
And then it dawned on me. THE CLOCK ON HIS COMPUTER WAS WRONG.
HE TESTED HIS CLOCK, AND IT WAS 12 SECONDS BEHIND!!!!!!!
Date.now() in JavaScript was getting the time from his OS, and his OS had a wrong clock! So the event was getting rejected as being too old.
Holy shit! I couldn't believe it! I still can't believe it! Months of anxiety and terror finally resolved by a fucking clock.
So my friends, remember that this Halloween, the scariest thing is not goblins and ghosts. it's time. Time ages us and destroys us. But it is a reality we have to live with. Let us embrace death! To accept the afterlife is to be free.
Thank you very much my spooky friends and have a Happy Halloween! ๐ป ๐ ๐
https://gitlab.com/soapbox-pub/ditto/-/merge_requests/576
Loading quoted postโฆ
Hello from #Ditto with #Amber!
10/21/2024, 6 engagements
#Ditto servers now have a SECRET NIP-46 bunker login page, accessed by navigating directly to /nostr/login on the server: https://gitlab.com/soapbox-pub/soapbox/-/merge_requests/3172
(It's only "secret" because I need it for R&D, but haven't taken the time to properly work it into the design of the login flow.)
10/20/2024, 4 engagements
If you go into the "Discover" tab of #Ditto, you can now infinitely scroll the top Nostr posts of all time through history!
Loading quoted postโฆ
10/20/2024, 4 engagements
Web Push Notifications have been implemented on #Ditto! ๐
10/15/2024, 1 engagement
Still recovering from ReplyGuy one month later, #Ditto has a puzzle captcha now.
We coded this captcha from scratch using the Canvas API, mainly based on work by
and implemented in Soapbox by
๐


10/11/2024, 2 engagements
Machine translations, coming soon to #Ditto ๐
10/10/2024, 4 engagements
People running #Ditto servers, if you pull from main you also need to upgrade Deno, because 2.0 has a few breaking changes. You also can't upgrade Deno without pulling from main.
Loading quoted postโฆ
#Ditto upgraded to Deno 2.0! ๐ https://deno.com/blog/v2.0
#Ditto language filtering!
Over 100 language are supported, with infinite scrolling.
Thanks to work by
and
, it's now possible to filter the global feed by language on #Ditto!
This helps people to more easily find posts in their native language. Portuguese, for example. ๐ง๐ท





Some university professor on crack invented something called the VAPID protocol I need to implement on #Ditto to enable Web Push Notifications, and I'm wondering how bad it would be to reuse my Nostr key for it. https://web.dev/articles/push-notifications-web-push-protocol
I improved relay hints etc on #Ditto so it embeds nprofile, nevent instead of the lazy ones, and adds NIP-10 pubkeys to e tags (among other fixes).
Auth tokens in #Ditto's database are now hashed, and nip46 keys are encrypted: https://gitlab.com/soapbox-pub/ditto/-/merge_requests/526
This means that even if someone gained unauthorized access to the database, they wouldn't be able to control people's sessions. In other words, much better #security on the server.
Loading quoted postโฆ
I reworked auth tokens on #Ditto. This caused users to get logged out. Sorry!
I have started working on letting #Ditto be a true PWA/TWA. First step is generating the manifest file dynamically: https://cobrafuma.com/manifest.webmanifest
It pulls in the same site title and description from the site configuration itself automatically.
What I'm missing is the ability for admins to upload icons in multiple sizes and configure that somewhere. Or better yet a built-in way to generate these icons from a source graphic.
#Ditto current event acceptance, "before replyguy" event acceptance, and "during replyguy" event acceptance.




Just deployed to my #Ditto servers: anyone in jack's WoT can post again without a nip05.
https://jsr.io/@gleasonator/policy/0.7.0/mod.ts#L38
That means anyone jack follows and anyone they follow will now be visible again on ditto.pub, gleasonator.dev, cobrafuma.com, etc even if they don't have a NIP-05.
Loading quoted postโฆ
Alright alright, I did it. #Nostrify gets a #WoT policy: https://gitlab.com/soapbox-pub/nostrify/-/merge_requests/103
Now on #Ditto I can seed some accounts to allow npubs again.
#PGlite support is fixed in #Ditto! https://gitlab.com/soapbox-pub/ditto/-/merge_requests/520
Relay Pool measurements in #Ditto. The donut chart is broken up by WebSocket readyState. Soon we will be connecting to more relays, but first I wanted better visibility.


Me and the boys
๐ฎ๐ท ๐ง๐ท ๐ฎ๐ณ โ #Ditto


You can search to see only Portuguese posts from cobrafuma.com NIP-05s. #Ditto


Finally, account search on #Ditto has been vastly improved, fixing autocomplete and showing people who you follow at the top! Thank you
!


#Ditto now supports NIP-50 language extension to allow filtering by language in search!



GM!
redesigned the zap modal in #Ditto featuring custom icons drawn by
!


#Ditto dashboard was updated to include Mastodon API metrics: https://grafana.com/grafana/dashboards/21844-ditto/ (revision 3)


#Ditto moderation policies have been upgraded to allow reading from the database, so now you can automoderate events based on its author, what it's replying to, etc.
Friendship ended with #SQLite. Now #PGlite is my best friend.
https://gitlab.com/soapbox-pub/ditto/-/merge_requests/485
#Ditto
I forked #pglite, learned about the Postgres wire protocol, fixed how it serializes query parameters ( https://github.com/electric-sql/pglite/pull/306 ), deployed my branch to npm and got pglite working in #Ditto.
#DItto will remove support for #SQLite in favor of #PGLite once the last few issues are fixed: https://gitlab.com/soapbox-pub/ditto/-/merge_requests/483
I made a simple #Ansible playbook to upgrade multiple #Ditto servers at once.



I fixed YouTube previews in #Ditto.
The solution... pretend to be Facebook instead of pretending to be WhatsApp. Seriously: https://gitlab.com/soapbox-pub/ditto/-/merge_requests/482
Loading quoted postโฆ
If you want to set up #Grafana with #Ditto, check out the guide here! https://docs.soapbox.pub/ditto/metrics
Our dashboard.json: https://gitlab.com/soapbox-pub/ditto/-/blob/main/grafana/Ditto-Dashboard.json



Amigos, preparamos um guia para as pessoas do #Brasil que estรฃo saindo do X para se juntar ao Nostr! Dรชem uma olhada.
https://soapbox.pub/blog/como-entrar-no-nostr-brasil-cobrafuma/
๐ ๐ฌ #Ditto
GM, we've created a Brazilian #Ditto server! We're still working on the name. We are looking for feedback, and to make connections with Brazilian influencers. This is our time to shine, Nostr!โ
Finally fixed the issue in #Ditto with apostrophes in mentions. It's kind of stupid that mentions are URIs, but oh well.


If you missed my speech on #Ditto at #Nostriga: give it a watch!

One thing I forgot to mention in my #Ditto presentation is that people could implement other APIs besides Mastodon. If someone did the Telegram API we could make Telegram clients work on Nostr with small modifications to them. Even better to implement Matrix/Element, which could mostly work without modifications.โ




Next version of Deno will support pglite. ๐คฏ If the performance is acceptable, I want to drop SQLite support in #Ditto and switch to pglite for local development.
https://github.com/denoland/deno/issues/25028#issuecomment-2293856707
Almost a reality: people with NIP-05's get a favicon by their name. Click it to open their domain feed. #Ditto



#Ditto admins: another big database migration: https://gitlab.com/soapbox-pub/ditto/-/merge_requests/453
This one uses a jsonb column to index tags, which works way better that what we did previously and saves a lot of space.
gleasonator.dev, ditto.pub, and henhouse.social have all been updated.
#Ditto will promote creation of a lot of new relays. I'm taking performance seriously.
Loading quoted postโฆ




I added a semaphore between the firehose and pipeline to flatten out requests to my database, and this is a perfect example of it working. Basically that the green line is shorter and wider than the blue line. I should probably decrease the semaphore size even more. #Ditto


The way I architected #Ditto is more like Primal. You just have a server that stores everything. This comes with it's own set of challenges, but I think they are easier to deal with.
Loading quoted postโฆ
Here is my latest #Ditto performance discovery. When query time goes up, available connections goes down. This is what's causing downtime. This is basic cause and effect, but it's good for my sanity to confirm this. Now I just have to figure out what's causing the query time spike.



#Ditto available database connections. Major L, but weirdly also a W. Been trying to diagnose this problem for a week, now I can fix it.


Graph of relay connections by verb. #Ditto #Prometheus


6/24/2024, 1 engagement
Are people having problems with #Nos2x and #Ditto? https://gitlab.com/soapbox-pub/ditto/-/issues/165
Likes and emoji reactions to my relay is a sawtooth wave. #Ditto #Prometheus


IT'S WORKING, IT'S WORKING!!! #Ditto #Prometheus


I am building out #Ditto Prometheus integration for sanity reasons.
Loading quoted postโฆ
I added rate-limiting to the REST API in #Ditto. Working on WebSocket limits next.
AntiPornPolicy for use with #Ditto or #Nostrify https://jsr.io/@gleasonator/policy/0.3.0/policies/AntiPornPolicy.ts
Simple yet effective.