Hacker Timesnew | past | comments | ask | show | jobs | submit | rvrb's commentslogin

Users in a Discord server/local community on tools like Discord naturally expect that their actions within that community are private in so far as they trust everyone in the community (including the operator) to keep it so.

By using ATProto, Colibri fundamentally makes all of your communication within any community completely public to everyone on the internet.

That’s fine for something like Twitter, where the product sets the expectation of such a thing. You can imagine how big of an issue this is when you try to do it in a trusted community model. Add on that Discord is used by kids who likely don’t know this and you can see why this is dangerous.

I consider this not only just a liability but bordering negligence. It is fundamentally broken, at an architectural level


I agree that is borderline negligence, and by far the biggest issue with AT and Bsky. Here is what I believe to be the most recent discussion on that topic:

https://github.com/bluesky-social/atproto/discussions/3363


Theres more recent updates on it in a blog post from Bluesky head of protocol: https://dholms.leaflet.pub/3mhj6bcqats2o


Very cool! Thanks for sharing that.


The current conversations are around how to do permissined data properly on atproto. I have a prototype, but Bluesky hasn't participated in the community effort and looks to be doing their own thing. They also took Bain Capital "funding" (private equity) which was the breaking point for me. They could have set up subs for nothing and made more than that, hard fumble imo.


Having something like circles from the Google+ days would be needed if ATProto is going to go anywhere. Is it possible in the protocol?


Yeah having the messages be e2ee by default and then extending it out to one or more groups depending in which circles are currently included for messages could let atproto act like an encrypted group chat with crisscrossing group chats per message, which can ratchet up and along with the new enceyption keys each message/batch of 10 messages/hour/day until that client is dropped from a group or a group is dropped from a conversation, then the keys change and pfs prevents old clients from continuing to read future messages.

Sure you can see that users emit messages in the pds but you dint know if its for your former group or other activitt


Fair point! A different user has already pointed out that this isn't disclosed enough on the landing page, and I'll be adding a section to clarify that, both on there and in the app itself.

I think one of the replies here already linked the current proposal for private data spaces, which I'm hoping will become implemented later this year. At that point, people will have the option of either having their community be 100% public, or confined to a more Discord-style data storage, where people can still join, but not everyone can "just read" the messages


Just want to chime in with, this does feel very slick, but this was the #1 question I had. I could not determine it from your site, and had to try it out to see.

One major criticism of things like Discord is that they're private, so I don't think that it's inherently disqualifying, some people might even prefer it for that reason. But it's very, very important that you're very clear about this, up front.


I really appreciate you chiming in, no matter how slick! New section has been added, lmk if you'd like to see this adjusted further


Much much better! Thank you!


The first assumption has been long disproved since multiple full scale Discord data leaks. If it's a public server, it can be scraped.

https://www.malwarebytes.com/blog/news/2024/04/billions-of-s...


any discord server that offers public invites is effectively public.


First, the user knows this when joining a public community.

Second, the moderators can choose to remove someone who has joined the community in bad faith.

Third, it is entirely different than broadcasting every single action taken by every single user in every single community on the entire protocol to anyone with one URL.


the moderators can choose to remove someone who has joined the community in bad faith

unless you prevent new members from reading the chat history until given permission then they can already read everything before they are kicked out, and they can come back with a different account.

you also can not detect people acting in bad faith if all they do is read.

basically, you can't expect privacy if you don't limit members to people you know and trust. that goes for any group chat, encrypted or not.

i also doubt that discord chatlogs are encrypted on their servers.


> the moderators can choose to remove someone who has joined the community in bad faith

This is one of the challenges of building a Discord alternative on atproto. Allow access or not, how moderation works, and having shared ownership that can change.


What is your point? I feel I made the one you are making before you even responded the first time.

That Discord communications can be exfiltrated in this specific set of circumstances (again, something I already said) does little to change that Colibri is implemented in the least privacy preserving way possible, short of publishing directly to every news and intelligence agency on your behalf, and does little to make that very clear in the first place.


you said: Users in a Discord server/local community on tools like Discord naturally expect that their actions within that community are private in so far as they trust everyone in the community (including the operator) to keep it so.

my point is: you don't get that in a public discord. and i believe that most discord servers, those for games anyways are public. only small team discord servers are private. privacy on discord is an illusion. i also would not trust discord to keep any messages private even from a private server.

you seem to imply that just by looking like discord colibri promises the same privacy options as discord. why? colibri does not present itself as a discord alternative. and although the line "privacy when needed" was misleading, in the FAQ they clarified that there is no private data. (to be sure i checked the site as it was 2 weeks ago: https://web.archive.org/web/20260311020805/https://colibri.s... )



> First, the user knows this when joining a public community.

From Colibri: your community chats are public and visible to everyone by default.

So it's the same.

> Second, the moderators can choose to remove someone who has joined the community in bad faith.

Colibri has mod tools as well.

> Third, it is entirely different than broadcasting every single action taken by every single user in every single community on the entire protocol to anyone with one URL.

Sure, but then just don't use it?

It's really no that different from how IRC worked. Except persistent history is part of protocol and not some bots.

This is not public communities, not for small group of friends sharing edgy memes and discussing national security.


Private channels in public servers exist. I'm almost entirely on private servers.


This is one of the challenging aspects about defining permissioned spaces on atproto. In essence, you have a completely separate database per user (sits next to their repo) with which you can do permissioned public->private spectrum. Nesting more privacy inside another permissioned space requires breaking the typical permission walking chain, eg. in Google Docs, if you have access to a folder, you have access to the subfolders.


Yea, and it's a protocol problem more than anything. It would not be difficult to bolt on some kind of permissioning system to a PDS, but nothing else in the system would know how to handle that.


[flagged]


> Bluesky solved the DM case by adding E2E encryption using the Signal protocol

This is patently false. Bluesky DMs are not E2EE, they do not use Signal.

Germ is the MLS based system that a few bluesky users are on, but it started separate from ATProto and has had account integration to atproto added on later. The folks behind that are a separate entity from Bluesky. I'm not keen on this setup, I'd prefer an MLS scheme where there are more controlling entities of the servers.

I agree E2EE chat is not the foundation for a Discord alternative and that Colibri has poor messaging and understanding. Communities need permissions, UX needs visibility into the data for things like search. E2EE has unsolved scaling problems required for real world communities.


Even with all that, you're leaking an unacceptable amount of metadata.

And what about reliability? If I cause the key to change, and then alter my PDS so it only shows that event to one half of users, did I completely mess up your protocol so you have to delete the chat room and start over?


MLS would be the primary standard for group messaging these days with the usual guarantees right? (PFS, backwards secrecy, etc) As I understand it from the RFC, large groups was an explicit design requirement and costs are supposed to be asymptotically logarithmic with group size, so I don't see why it couldn't be used. I feel like Colibri (based on their page) just doesn't believe it's there problem, which seems... irresponsible.


Bluesky DMs aren't end to end encrypted. Where are you getting that impression from?


He is a bot.


Binary space partitioning


`extern` and `packed` container types have well defined layouts. a regular `struct` is an "auto" layout - and the compiler can and will rearrange whenever it wants.

if you need a well defined layout, use `extern`. if your struct makes sense to represent as an integer, use `packed`. I think it is often ill advisable to use `packed` otherwise.

you can explore this yourself on the Type info returned from @TypeInfo(T):

https://ziglang.org/documentation/master/std/#std.builtin.Ty...

https://ziglang.org/documentation/master/std/#std.builtin.Ty...

https://ziglang.org/documentation/master/std/#std.builtin.Ty...


To wit: https://ziglang.org/documentation/master/#extern-struct

> An extern struct has in-memory layout matching the C ABI for the target.

Zig is really good at speaking the C ABI of the target, but the upshot seems to be that it appears there is no stable Zig-native ABI.

If I'm correct, I wonder if there are plans to settle on a stable ABI at some point in the future. I do know that in other languages the lack of a stable ABI is brought up as a downside, and although I've been burned by C++ ABI stability too many times to agree, I can understand why people would want one.


I doubt zig will have stable abi any time soon. It may have some sort of "zig extern" when it gets mature. But stable abi isnt very usful if no-one else can talk it. I have project that uses codegen to effectively implement zig like ABI on top of the C abi.

Heres the kind of code it generates https://zigbin.io/6dba68

It can also generate javascript, heres doom running on browser: https://cloudef.pw/sorvi/#doom.wasm


Andrew Kelley has said relatively recently that there are no plans to introduce a Zig ABI: https://github.com/ziglang/zig/issues/3786#issuecomment-2646...


What's interesting is that the scope of the proposal isn't a Zig-specific ABI, but a codified way of expressing certain Zig concepts using the existing C ABI.

That could be an interesting middle ground.


Yeah the new translate-c package already kind of does that.


in practice, as long as you match the version and release mode, it's fine (though you are playing with fire). I pass raw pointers to zig structs/unions/etc from the zig compiler into a dynamically loaded .so file (via dlload) and as long as my .so file is compiled with the same compiler as the parent (both LLVM, in my case) it's peachy keen.


You are still playing with fire as the data inside those pointers may be different even if they are the same type. Zig is free to optimize them in anyway it likes depending on the code that touches them (aka its free to assume they never leave the program).


I haven't verified this, but I would be willing to bet that most of Bun's issues here have more to do with interfacing with JavaScriptCore through the C FFI than Zig itself. this is as much a problem in Rust as it is in Zig. in fact, it has been argued that writing unsafe Zig is safer than writing unsafe Rust: https://zackoverflow.dev/writing/unsafe-rust-vs-zig/


As someone who has researched the internals of Deno and Bun, your unverified vibe thoughts are flat out wrong. Bun is newer and buggier and that's just the way things go sometimes. You'll get over it.


you say this like it's some own or something, but I'd be more surprised if they didn't. believe it or not, they are already donating some of their funds to the upstream ecosystem[0].

[0]: https://ziglang.org/news/2025-financials/


Zig is a tool that helps professionals prevent those mistakes.

"Memory safe languages" are tools that prevent professionals from making those mistakes.

It's a subtle but important difference. Zig attempts to leave some humanity to the developer.

When someone says they are incapable of earning or deserving that, I feel sad.


It's not that subtle, the thing that we knew doesn't work still doesn't work.

Some of our problems are novelties because before Grace Hopper basically nobody is writing software so there aren't centuries of lessons in how to do it properly - but this problem isn't like that, all the safety critical industries could tell you that "professionalism" won't prevent the mistakes and what you need is mechanism so that the mistakes cannot happen.

Let me give you an example I like from the railways, which are about twice as old. One night, signaller comes on duty and during the day a team of engineers have been doing work on "his" signal box. Still, things seem to check out and he gets to work, a train approaches, he tries to give them their whole route but their next signal seems stuck and won't "pull off" from danger - he blames the engineers of course. In accordance with regulations the train's driver phones the signaller, signaller explains that he can't release the signal but gives the driver authoriation, per standard instructions, to pass only one signal and proceed at caution (ie slowly enough to stop short of any obstruction) to the next signal. The next signal though is the same, the signaller is annoyed, blames the engineers again, same order to proceed at caution. The next signal is the same again. But, just after the driver receives their authority and passes that signal the signaller gets another call. Funny, surely they haven't reached the next signal yet? No. They're face-to-face with another train. Some junction points ("switch" if you're American) have failed, the train has been sent into another, both stopped short and nobody is injured.

The points failure had been detected. If the signaller had carefully checked his instruments they'd have told him that this failure had occurred and that is why it wasn't clear to set those danger signals off which is why, try as hard as he could, they could not be pulled off. It is mechanically impossible, not because of professionalism, or capability or any other brave words but physically impossible to kill everybody by clearing the conflicting signals in this state.

Mechanism.


Creating a language which is difficult to use and dangerous is not “lending humanity to the developer”. Humans make mistakes, and a language that doesn't account for this is ignoring the humanity of its users.


Zig is famously simple to pick up and write with, so I don't know what you mean by "difficult". Software is dangerous. Memory safety is one of a million ways it can be dangerous. A compiler barfing when it thinks you are doing something unsafe with pointers is one approach to dealing with one of the ways that code can be dangerous to execute.

Zig does not ignore that particular danger, it just takes a different approach to dealing with it than some other modern languages. An approach that, I believe, leaves the developer with a little more humanity by allowing them the benefit of the doubt that they know what they are doing.

Everyone that has not built a systems language, or has not built a real application with both Zig and a memory safe language, that is reacting emotionally to what I've said should put a lot of consideration into whether they are cargo culting or using critical thought. Consider that we still do not yet know what is best, and shutting down attempts to explore different ideas with things like "creating [and using] this language is ignoring the humanity of the end user" is, well.. dumb.


>Zig is famously simple to pick up and write with, so I don't know what you mean by "difficult".

It's easy to get in a car and put your foot on the pedal, but usage entails not crashing.

>Memory safety is one of a million ways it can be dangerous.

We have the statistics on this. It is 7 out of 10 ways it is dangerous, going by proportion of CVEs, so it's likely higher in less well tested software. Your estimation was off by 5 orders of magnitude.

>leaves the developer with a little more humanity

I could care less if the developer is afforded humanity. I want to write software and I want a programming language that helps me to do that. Whatever humanity I'm sacrificing by writing in memory safe languages is more than made up for by the comparative ease of not having to worry about memory safety.

>reacting emotionally

You are reacting emotionally. You are judging programming languages by their emotional value rather than their features. “Humanity” is not a measurable feature. 70% fewer CVEs is a measurable feature.


I have never heard anyone calling Zig "famously simple" before. In fact, people tend to say it has quite the learning curve.

Of course, caveats apply: it is certainly simple compared to some languages, but certainly not compared to others. "Famously simple" seems to indicate it is one of the simplest languages to learn, which seems wrong unless there are some serious qualifications to that statement?


C, C++, Rust, Zig. Rust and C++ have an infamous learning curve. If you know anything about using any systems language other than Zig, Zig is incredibly simple to pick up, like C. Unlike C, it pushes you toward making less mistakes.

If you don't know anything about using a systems language, Zig makes it easier for the people who do to review your code and make sure you didn't mess it up. It does this with very intentional design that makes it easier to understand the full impact of code quickly, reducing the cost of review, making review practical to catch the issues. It also has many other fail safes to catch these problems before they ever reach a production release.

So, yeah, it's totally depending on where you are coming from -- but Zig is not a tool built for a web developer who doesn't know anything about memory to go and ship an application within their first week. It does make it easier for that person to learn the ropes at a steady pace.

Meanwhile, everyone complaining that Zig is not memory safe doesn't seem to care that applications written in Zig do not have the vulnerabilities that memory safety solves on the scale that C does[0].

If you have not written a real application in Zig and evaluated it for vulnerabilities, but are claiming that creating Zig was irresponsible, and using it is too; you are cargo culting.

If you have, you probably understand there is a niche that Zig fits in and that it isn't surprising it exists to fill it. Like all things in our industry, there is a cost/benefit analysis required for choosing the tools you build with.

No one reasonable has claimed that memory safe languages should not exist, but there is a maddening number of people being disrespectful toward those who think there are other ways of addressing the same problems.

[0]: https://mitchellh.com/writing/ghostty-gtk-rewrite


If you are comparing Zig to Rust and C++, which are very well known to be difficult to learn, then that is not really saying anything about the ease of learning it. Compare it to Swift, D or Odin. Is it "incredibly simple" compared to those languages as well?

Similarly, one can claim that pretty much anything compiles "incredibly fast" if one compares with Rust, C++ and Swift.

But comparing to worst in class doesn't actually say anything.

One note about this:

> If you have not written a real application in Zig and evaluated it for vulnerabilities, but are claiming that creating Zig was irresponsible, and using it is too; you are cargo culting.

I don't know what this has to do with my comments at all, but I want to point out that you are using "cargo culting" wrong. This describes imitating practices of something successful, thinking that by this imitation, success will follow as well.

> No one reasonable has claimed that memory safe languages should not exist,

Again, I have not talked anything about whether memory safe languages should or should not exist. You are confusing me with someone else.


Marketing speak, that looks to be trying to put Zig over languages like Golang, Vlang, etc... which are well known for their simplicity and readability. The false hype arguably creates confusion, that Zig can be "all things to all people", when it's not.


the answer I've seen when it has been brought up before is that (for allocators) there is not a practical impact on performance -- allocating takes way more time than the virtual dispatch does, so it ends up being negligible. for code bloat, I'm not sure what you mean exactly; the allocator interface is implemented via a VTable, and the impact on binary size is pretty minimal. you're also not really creating more than a couple of allocators in an application (typically a general purpose allocator, and maybe an arena allocator that wraps it in specific scenarios).

for IO, which is new and I have not actually used yet, here are some relevant paragraphs:

  The new Io interface is non-generic and uses a vtable for dispatching function calls to a concrete implementation. This has the upside of reducing code bloat, but virtual calls do have a performance penalty at runtime. In release builds the optimizer can de-virtualize function calls but it’s not guaranteed.
  
  ...
  
  A side effect of proposal #23367, which is needed for determining upper bound stack size, is guaranteed de-virtualization when there is only one Io implementation being used (also in debug builds!).
https://kristoff.it/blog/zig-new-async-io/

https://github.com/ziglang/zig/issues/23367


He's talking about passing the pointers to the allocators and Io objects as parameters throughout the program, not how allocator vtables for calling the allocator's virtual functions are implemented. But context pointers are a requirement in any program. Consider that a context pointer (`this`) is passed to every single method call ... it's no more "code bloat" than having to save and restore registers on every call.


out of curiosity, what feature do you want?


The feature I want is multimethods -- function overloading based on the runtime (not compile time) type of all the arguments.

Programming with it is magical, and its a huge drag to go back to languages without it. Just so much better than common OOP that depends only on the type of one special argument (self, this etc).

Common Lisp has had it forever, and Dylan transferred that to a language with more conventional syntax -- but is very near to dead now, certainly hasn't snowballed.

On the other hand Julia does it very well and seems to be gaining a lot of traction as a very high performance but very expressive and safe language.


I think this is a major mistake for Zig's target adoption market - low level programmers trying to use a better C.

Julia is phenomenally great for solo/small projects, but as soon as you have complex dependencies that _you_ can't update - all the overloading makes it an absolute nightmare to debug.


For what it's worth, that hasn't been my experience with Julia – I've found it easier to debug than Python, Scala, or Clojure (other languages I've used at jobs.)

The tooling makes it easy to tell which version of a method you're using, though that's rarely an issue in practice. And the fact that methods are open to extension makes it really easy to fix occasional upstream bugs where the equivalent has to wait for a library maintainer in Python.

500kloc Julia over 4 years, so not a huge codebase, but not trivial either.


Ada has them, and I guess we all agree on its systems programming nature.


NOOOO!

What Ada (and Rust) calls generics is very different -- it is like template functions in C++.

In those languages the version of the function that is selected is based on the declared type of the arguments.

In CLOS, Dylan, Julia the version of the function that is selected is based on the runtime type of the actual arguments.

Here's an example in Dylan that you can't do in Ada / Rust / C++ / Java.

    define method fib(n) fib(n-1) + fib(n-2) end;
    define method fib(n == 0) 0 end;
    define method fib(n == 1) 1 end;
The `n == 1` is actually syntactic sugar for the type declaration `n :: singleton(1)`.

The Julia version is slightly more complex.

    fib(n) = fib(Val(n))
    fib(::Val{n}) where {n} = fib(n-1) + fib(n-2)
    fib(::Val{0}) = 0
    fib(::Val{1}) = 1
    
    println(fib(30))
This is perhaps a crazy way to write `fib()` instead of a conventional `if/then/else` or `?:` or switch with a default case, but kinda fun :-)

This of course is just a function with a single argument, but you can do the same thing across multiple arguments.

    define method ack(m, n) ack(m-1, ack(m, n-1)) end;
    define method ack(m == 0, n) n+1 end;
    define method ack(m, n == 0) ack(m-1, 1) end;


You missed the way Ada does OOP, and went completely overboard talking about generics.

As you can see from my comment history, I am quite aware of CLOS, Lisp variants and Dylan.


Last I checked, Ada does not have multimethods/generic functions in the sense of CLOS, Dylan and Julia. It has static function overloading, and single-argument dispatch, just like C++.


>The feature I want is multimethods -- function overloading based on the runtime (not compile time) type of all the arguments.

>Programming with it is magical, and its a huge drag to go back to languages without it. Just so much better than common OOP that depends only on the type of one special argument (self, this etc).

Can you give one or two examples? And why is programming with it magical?


For a start it means you can much more naturally define arithmetic operators for a variety of built in and user-defined types, and this can all be done with libraries not the core language.

Because methods aren't "inside" objects, but just look like functions taking (references to) structs, you can add your own methods to someone else's types.

It's really hard to give a concise example that doesn't look artificial, because it's really a feature for large code bases.

Here's a tutorial example for Julia

https://scientificcoder.com/the-art-of-multiple-dispatch


Thanks.


Erlang/Elixir also has that


Something akin to interfaces, but weaker. Right now people roll their own vtables or similar, and that's fine...I actually don't expect these to be added. But because of Zig's commitment to "everything structural is a struct", a very very simple interface type would likely end up being used more like ML's modules.

The need for this jumped out at me during Writergate. People had alot of trouble understanding exactly how all the pieces fit together, and there was no good place to document that. The documentation (or the code people went to to understand it) was always on an implementation. Having an interface would have given Zig a place to hang the Reader/Writer documentation and allowed a quick way for people to understand the expectations it places on implementations without further complications.

For Zig, I don't even want it to automatically handle the vtable like other languages...I'm comfortable with the way people implement different kinds of dynamic dispatch now. All I want is a type-level construct that describes what fields/functions a struct has and nothing else. No effect on runtime data or automatic upcasting or anything. Just a way to say "if this looks like this, it can be considered this type."

I expect the argument is that it's unnecessary. Technically, it is. But Zig's biggest weakness compared to other languages is that all the abstractions have to be in the programmer's head rather than encoded in the program. This greatly hampers people's ability to jump into a new codebase and help themselves. IMO this is all that's needed to remedy that without complicating everything.

You can see how much organizational power this has by looking at the docs for Go's standard library. Ignore how Go's runtime does all the work for you...think more about how it helps make the _intent_ behind the code clear.


not being a direct competitor to either of these already existing languages is exactly why it is interesting!


I've tried writing a similar post, but I think it's a bit difficult to sound convincing when talking about why Zig is so pleasant. it's really not any one thing. it's a culmination of a lot of well made, pragmatic decisions that don't sound significant on their own. they just culminate in a development experience that feels pleasantly unique.

a few of those decisions seem radical, and I often disagreed with them.. but quite reliably, as I learned more about the decision making, and got deeper into the language, I found myself agreeing with them afterall. I had many moments of enlightenment as I dug deeper.

so anyways, if you're curious, give it an honest chance. I think it's a language and community that rewards curiosity. if you find it fits for you, awesome! luckily, if it doesn't, there's plenty of options these days (I still would like to spend some quality time with Odin)


+1 for Odin. I wrote a little game in it last year and found it delightful.


I prefer Odin to Zig after trying both... but it seems Odin's performance is a bit lower than Zig, C and Rust?! Have you noticed any performance issues or it's not something to worry about?


No, I write Odin for production and there is no performance difference to speak of coming from the way the compiler or language works. If you have one it's likely because of an older/different LLVM version being used, but AFAIK Odin stays as up-to-date as you can without tearing your hair out (and that's good because GingerBill has none of that to spare).

There might be a few pathological code paths in the core libraries or whatever for certain things that aren't what they should be, but in terms of the raw language you're in the land of C as much as with any of these languages; Odin really doesn't do much on top of C, and what it's doing is identifiable and can be opted out of; if you find that a function in a hot loop is marginally slower than it ought to be, you can make it contextless, for example, and see whether that makes a difference.

We haven't found (in a product where performance is explicitly a feature, also containing a custom 3D engine on top of that) that the context being passed automatically in Odin is of much concern performance-wise.

Out of the languages mentioned Rust is the one I've seen in benchmarks be routinely marginally slower, but it's not by a meaningful amount.


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: