Rust in Production

Matthias Endler

Tweede Golf with Folkert de Vries

Matthias Endler and Folkert de Vries discuss Rust's role in modernizing infrastructure, including its use in the Roc compiler and time synchronization protocols. They highlight the value of Rust in reliable software and upgrading existing users.

2024-02-08 73 min

Description & Show Notes

Have you ever wondered how computers stay in sync with the time? That is the
responsibility of the Network Time Protocol (NTP). Around since 1985, NTP is one
of the oldest protocols still in use on the internet and its reference
implementation, ntpd, written in C, is still widely used today.

That's a problem. C is a language that is not memory safe, and ntpd has had its
share of security vulnerabilities. Here is a list of CVEs.

As part of Project Pendulum, Folkert de Vries and his colleagues from Tweede
Golf have been working on a Rust implementation of NTP. I sit down with Folkert
to talk about the project, the challenges they faced, and the benefits of using
Rust for this kind of project.

Along the way, we learn about funding open source projects, the challenges of
establishing a new implementation of a protocol, and all sorts of other
interesting things that might or might not be related to NTP.

The Network Time Protocol (NTP) is a cornerstone of the internet. It provides accurate time synchronization on millions of devices, but its C-based implementation, which dates back to the 1980s, is showing its age. In this episode, we talk to Folkert de Vries, Systems Software Engineer at Tweede Golf, about their work on reimplementing NTP in Rust as part of Project Pendulum.

ntpd-rs
is an open-source implementation of the Network Time Protocol, completely written in Rust with the goal of creating a modern, memory-safe implementation of the NTP protocol.

Funding for the project came from the Internet Security Research Group and the Sovereign Tech Fund.

About Tweede Golf

Tweede Golf is a Dutch software consultancy that specializes in safe and privacy-friendly software. They work on projects that are critical for creating a safe internet infrastructure, protecting citizens' privacy, and securing connected devices with Embedded Rust.

Tweede Golf is also an organizing partner of RustNL, a conference about the Rust programming language, which takes place in the Netherlands.

About Folkert de Vries

Folkert is a Systems Software Engineer at Tweede Golf, where he works on low-level protocols that ensure the safety and security of the internet and devices connected to it. He is an open source maintainer and polyglot, working with and extending languages as diverse as Rust, Elm, and Roc.

Links From The Show
Official Links

Transcript

This is Rust in Production, a podcast about companies who use Rust to shape the future of infrastructure. My name is Matthias Endler from corrode, and today we are talking to Folkert de Vries from Tweede Golf about how to modernize critical time-sinking infrastructure with Rust. Folkert, welcome to the show.
Folkert
00:00:21
Thanks.
Matthias
00:00:23
Can you just briefly talk about yourself and about your role at Tweede Golf?
Folkert
00:00:30
Sure. So I've been using Rust for about four years. In my spare time, I mostly work on the Roc programming language, where I'm one of the main contributors. And it's been a really cool place to try out a lot of low-level stuff. And then at work at Tweede Golf, I mostly work on systems programming projects, decently low-level, and in recent years, mostly on our big open-source projects.
Matthias
00:00:56
When you mentioned Roc, I know it's a Rust podcast, but maybe people might still be interested in what it is. Can you maybe say a few words about that and where Roc comes from?
Folkert
00:01:08
Yeah, it's a pure functional language. So it is in that tradition. It's mostly, well, it's sort of a combination of like Haskell and Elm are sort of the two main languages. And really what makes Roc different is that it compiles to native code. And so occasionally we also just compile straight to assembly. That's a fun skill to have. And we want to have a very high performance ceiling for this language. So functional programming is typically associated with, it might look kind of fancy, but it's kind of slow, but actually with recent sort of research, we're able to create, you know, like to eventually output very fast code.
Matthias
00:01:51
You mentioned Haskell, you mentioned Elm I wonder where Rust fits in there like you still seem to be in the in the Rust space you still seem to be working with Rust how does Rust tie into that picture.
Folkert
00:02:06
This is interesting so like when we when we wanted to make the raw compiler uh sort of the obvious choice would have been Haskell given that we were functional programmers we quite liked Haskell, but we had seen some existing Haskell projects and we just knew that, In terms of performance, it wasn't the best that it could really be. So we knew that in theory with Rust or with like a low-level language, we could do like, you know, SIMD and various other kinds of optimizations. We had no idea how to do any of them, right? Like we were functional programmers and had heard about this sort of optimization stuff, you know, in university or other places, but we hadn't actually experienced that. And so in practice, C++ would have been a non-starter for us. But with Rust, we felt right at home, given its type system that was very familiar. And then, you know, the rest of the language is sort of imperative. But, you know, you are exposed to that anyway. We know how to write a for loop, right? So that is sort of how we got started with Rust. And, you know, sometimes we look back at some of the old code, and it's quite bad in terms of, you know, idiomatic Rust. But over time, we got a lot better and we actually sort of descended down to the lowest level of the computer where we're now reasoning about like cache locality and using SIMD and minimizing the number of syscalls, that sort of stuff.
Matthias
00:03:35
When you explained that, I was wondering if you used LLVM as a backend. So for the uninitiated, LLVM is a compiler backend infrastructure that, for example, powers Rust as well. Well, is that the same for Roc-lang, or do you have your own compiler backend? I know for the people that are listening and are not too technical, apologies for the detour, but I'm just genuinely curious about it.
Folkert
00:03:59
Yeah, so we have different modes, and this is in fact something that Rust will also soon have that many languages are now looking at. So LLVM is really great for getting the best final output. put. So if you want a sort of super optimized program, LLVM is basically the state of the art. The problem is that most of the time when you compile your code, you don't really need that level of optimization. You really just want to check whether the change you made sort of fixes the test or, you know, like the program now runs correctly. And then LLVM, the sort of trade-off that it makes is sort of a bad one for you because it spends a lot of time optimizing a program. And even if you ask it to sort of not do any work, it's still very slow. And, you know, you will will notice this all the time with Rust, where compile times are very slow. And often, this is really not because of the borrow checker or anything. That is compiler code written by Rust developers, and they can make that fast. The problem is usually LLVM, or a significant part of it is LLVM. So a lot of languages are looking at alternative backends for development. We have custom assembly backends for x86, AARCH, and WebAssembly. They are pretty much on par now with the LLVM, but they are like blazingly fast. So Roc can actually compile and link and then run Hello World faster than Python can print it, which is about like, yeah. So like that is the sort of speed that your computer can actually achieve if you don't use LLVM. We can beat LLVM, you know, Like thousands of hours of labor have gone into sort of having that produce very fast programs. But it just makes the tradeoff of like, we're going to set everything up to optimize. And then if you decide not to do that, you've wasted a lot of time and resources, basically.
Matthias
00:05:57
Isn't it also true that if you are at a stage where you can compile a reasonably sophisticated language, maybe that is similar to Haskell or Elm to some extent, I'm talking about Roc in this case, if you have such a backend, couldn't you also use it for Rust, which is also in this ML sort of space of languages, similar to, it's pretty similar to Haskell. Could you use the backend for Rust at some point?
Folkert
00:06:25
It's, that would require quite a bit of additional work, I think. Like it's really specialized towards the sort of operations that Roc allows for. Rust has a lot more primitives, ultimately, that you would, like in particular, things around pointers, right? That you would need to somehow translate. It's totally feasible. It's like an extension that you could make. But Rust is currently looking at the Cranelift backend, which is really cool. There's a lot of sort of cool research that goes into how that one works. And then also the GCC backend, which I believe is mostly about supporting more embedded targets, but potentially also has performance improvements over the LLVM backend.
Matthias
00:07:07
Rust does not have a garbage collector. Does Roc have one? And if so, how does it impact the language design or the compiler design? design so.
Folkert
00:07:16
In Roc we use reference counting as our garbage collection strategy so we don't have a garbage collector in a classic sense where you run into like garbage collecting pauses and things like that effectively like everything is wrapped in an arc right and in rust terms and so cloning is cheap but you do need to like that that reference count needs to be updated all the time so there is a certain overhead associated with that also all of your allocations are slightly slightly bigger. You need space for that reference count to live. But generally speaking, that is a very, like sort of good trade-off to make. We get very reliable performance. You need something like this anyway. And then for a functional language in particular, we also get mutation. So normally in a functional language, you have this like referential transparency idea where you can actually update or mutably update any values. So that means a lot of cloning unless you're smart, but we can actually allow for in-place updates. Basically when the reference count is one you get a immutable reference basically because there is just a single owner of that value and you can you can make updates.
Matthias
00:08:31
Feels like we went a bit ahead of ourselves sure we kind of jumped right in and I kind of would like to tie it back into the topic of this podcast which is about NTP and other protocols which are useful for various things like timing. But I guess before we jump into that, maybe we can take a quick step back and say the overarching theme of what we talked about so far is, in my opinion, performance and understanding how computers work. Is that also what motivates you to work on these problems?
Folkert
00:09:09
Yeah, it's really, you know, it's like I now end up being able to work with a lot of things that I never really thought I would have worked on in university, for instance. So I was this very sort of Haskell-y programming language theory sort of person. And then Rust very naturally allowed me to sort of come into contact with a lot of these low-level concepts, right? Like, you can use Rust in a way that is quite similar to Python in a way. In practice, people will quibble with this. But I think it's sort of true. Like, you don't need to understand a lot about the machine to still write Rust code. And then in practice, it is actually quite performant. But very slowly, right, you click through the documentation, you read some stuff, you click on go to source a couple of times, and you will end up at these places where a lot of low-level details actually start to matter. And I think this is really nice about Rust that like go to source generally works even into the standard library. It's like occasionally you hit some macro and that is kind of tricky, but generally you will find something that makes sense and you can learn about it. This is very different in say Haskell or Python or Java or whatever, where the primitives are implemented in a different language and you have no idea how they work.
Matthias
00:10:31
I kind of share the same background. Maybe I wasn't as deeply involved in Haskell as you, but back in university, I did some Haskell. And for some mathematics departments, I wrote some algorithms and implemented some geometric functions. Functions and what i felt was when i was introduced to rust at some point i realized that it's real world Haskell or it's pragmatic Haskell to some extent and it felt like i could actually use all of the things that i learned from Haskell type theory and algebraic data types and so on in a real world project did it feel the same to you yeah.
Folkert
00:11:15
It's well like i think if you come at it with that background where the type system isn't actually that intimidating to you. And there's just some like syntax to learn, you know, like what is a trait? What is an impl block? That sort of stuff, right? Like you can pick that up very quickly. And so I think with that background, Rust isn't nearly as intimidating as when you are sort of coming from say like a Java or a Python background where Rust's approach to types and then lifetimes is is quite different from what you're used to. Like to me, it made total sense. Like, okay, sure, we have a problem. We're going to use the type system to sort of make that problem go away. Great. Like I know exactly how that sort of general form of solution works. And so that was very comfortable. But then like, as you say, like actually it is much easier to get practical stuff done with Rust. I also just think that it helps that Rust is a much more modern language. Which Haskell really shows sort of signs of aging at this point. It is over 30 years old. Around that sort of age, it just shows. It shows in the standard library. It shows in an accumulation of features that we don't use anymore and have to caution people to not use. And so Rust has a sort of major advantage there in that it was able to learn really from Haskell, from ML, from C++, from all the other languages.
Matthias
00:12:44
Early on, you mentioned that Rust made it really accessible for newcomers to learn about the underlying implementation and you mentioned that you can click on view source in the documentation i do think that this is an amazing feature are there other features like that that are just maybe a little more hidden that people might not know about that maybe also enable you to learn how rust works under the hood or other things that make the language more accessible to newcomers i.
Folkert
00:13:18
Think With regards to how things work, I think it's also something that is sort of valued in the Rust community that this sort of low-level detail is sort of appreciated. Like in a lot of other languages, people will say, oh, don't worry about that. The runtime will take care of it. Perhaps even the compiler will take care of it. But of course, in Rust, we know if you actually build the compiler or are quite close to it, you will know exactly what limitations it has. And also, you will know how fun it is to actually put a bunch of bytes in the right order and have that constitute a program, right? The sort of like hacking around with and really playing with computers, really commanding the machine, I think is something that you can certainly find in the Rust community. Community you know a couple of other communities as well at this point like it's something you don't see all that often but that excitement about really commanding your hardware is that is really cool.
Matthias
00:14:20
Yes and there's this other notion of explicit versus implicit where a lot of things in rust are not hidden they are visible in plain sight for example how vectors are implemented or or what allocates and what doesn't allocate.
Folkert
00:14:37
Yeah, I was particularly... Well, I think a cool example of this is the mutex synchronization primitive, which I'm sure I've seen that in universities somewhere. Some professor must have mentioned it at some point, like this is a mutex. But I don't think we ever really got to play with it. And I certainly didn't understand how it actually works. Like, how does this mutex know whether anyone else is currently also looking at the same piece of data? How does it get notified that the lock is now available and it can be taken by the current thread? And in Rust, you can actually just go to source and it's right there. It's a bunch of more primitive values and you actually can understand how it works. Like most other languages don't have this. Like even in C or C++, if you go to the source code of like the libc or the standard library, it is incredibly arcane. It looks very different from sort of standard C source code, in my view anyway, as not a very experienced C programmer. Like that stuff just looks really weird. And I mean, in Rust, you can just find something, find an implementation that makes sense.
Matthias
00:15:52
That is very true. I can still remember that back in the day, we needed to use mutexes in C, for example. And the one thing that still pops up or still top of mind is deadlocks. And I do think that you could run into a lot of these situations with Rust as well. I mean, it doesn't save you from deadlocks because essentially it's a logic error, I would say. but at least if you use high level primitives it becomes much much harder to use them wrong.
Folkert
00:16:28
Yeah i i also think you like in rust it'll be really clear when you need a mutex right whereas i think in c it might be much harder to know like okay i'm using this value over here but is some other thread also touching this right now like you you sort of have no idea whereas in rust a the type checker will tell you, right? Like, oh, you're using this. Or, well, first of all, you have an immutable reference, so you can't mutate that thing ever. If you have a mutable reference, then you know it isn't shared between multiple threads. And if you do want something that is both shared and mutable right now, you need to wrap it in a mutex. Then, of course, there's still logic errors you can make.
Matthias
00:17:11
That's one of my pet peeves with C, because even if it looks extremely explicit about various things, it is actually quite implicit about what it does under the hood. So a lot of things, a lot of details are conventions, or you need to be aware that things return errors and so on. In Rust, that is very rarely the case. So it's the right kind of explicit, if you like. You don't need to deal with memory management, which technically is also possible in C, and you can be very explicit about it, but it allows you to make decisions which can impact the program's design.
Folkert
00:17:50
Yeah, for sure. Sure. I think it works out really well, the sort of approach to, well, yeah, not exactly preventing that blocks, but certainly making them more or like less likely to happen.
Matthias
00:18:06
It feels like, imagine we have a conversation like this and there's someone from the business side, maybe a CTO or a decision maker listening to us. They might think these are problems that only nerds care about or people that are highly technical at least how do you bridge the gap between technology and business then because at some point you need to be able to sell that and maybe explain why rust or a technical solution is superior or gives you a business advantage yeah.
Folkert
00:18:42
I mean like we would need to look more at like i guess like concrete cases but it's true right like i need to eat and so like money needs to come in at some point from someplace. So I think you can ultimately distill this into, you know, value for users. A lot of this low level stuff is really about user experience. If you want to think about it that way, right? Like performance is ultimately user experience. And I think we don't really appreciate that enough, but there's a lot of research from like Google, right? About like if a page load is this many milliseconds longer than this percentage of people will just like lose interest, right? This is very well established. In practice, a lot of software is really slow and only people sort of excited about this sort of low level stuff can fix that. And when they do, it's really it's like magic kind of. There's been a couple of recent very interesting also Rust projects, actually, like I think there is a Python linter right now that is like thousands, like a thousand X faster than some of the other tooling in the Python space. Like that is magic, right? For a Python programmer, like how did that happen? But it happened because a couple of systems people got very excited and felt like, hey, we can actually do much better at this task than the existing implementations. Let's go do it. And like, yeah. And then imagine the sort of like value that that actually has, the sort of multiplier of this one person going really deep down into that rabbit hole, coming up with a more efficient implementation. limitation. But then all users of that tooling, and of course, that tooling, like its usage is spreading very quickly, because 1000x improvement is ridiculous. You know, the sort of the amount of time saved, the amount of extra productivity gains is really something that is almost hard to even capture, right? So like, that is ultimately the power of this low level stuff. Like, yes, Yes, it is very nerdy. Yes, it is very, it seems to be very distanced from sort of the real world sometimes, but it has real outputs.
Matthias
00:20:57
But at the same time, if you approach me and you said, oh yeah, we will have a 10x or 100x improvement in performance. And I ask you, how long will it take? And you say, yeah, it will take five or 10 years to get there. I'm not sure if that convinces me right there because it's a big time investment and it might be even hard to quantify.
Folkert
00:21:21
That that's true so you really need to pick your battles there there is just certain certain areas certain problems where this sort of approach works well for some tasks like it it really doesn't matter and you know like in our spare time we might still They'll play around with optimizing the last couple of milliseconds or whatever, but it doesn't have a lot of business value. You need to be strategic about this, for sure.
Matthias
00:21:51
Tweede Golf Folkert, at Tweede Golf, you do a lot of open source work, I assume. What does open source work look ? How do you pick a project? How do you get started? like at Can you name a few examples that you worked on? How does that general process look like?
Folkert
00:22:09
Yeah. We're a very small company, really, in a very unknown city in the eastern part of the Netherlands. But we do a lot of, I think, very interesting and sort of high quality work. So historically, mostly in the web space, but then also now doing embedded. We do a bunch of training and then we work on a couple of these like big open source Rust projects right now. So like, you know, we've always liked open source and might have contributed occasionally to, you know, something or other. But the big projects that we run now are sort of big funded open source projects such that we can actually dedicate a good amount of like labor to them. And so the current main ones are projects around sudo, ptp, and ntp, where sudo is a sort of super user do command on Linux. Obviously very sort of security critical that that implementation is sound. And in that case, it also sort of needs to mirror the behavior of the existing implementation. And then P2P and N2P stand for the Precision and Network Time Protocols. And these are sort of important building blocks of the Internet. And so that is sort of the main reason that those projects originally got started. So sudoand NTP are funded originally by the Internet Security Research Group Prossimo through their Prossimo initiative. These are the people behind Let's Encrypt. These are people that are very passionate about the Internet and sort of preparing it for the future. And this initiative is basically their project to fund rewrites or implementations of foundational building blocks of the Internet in memory-safe languages, languages, which in practice is a euphemism for Rust. But for funding purposes, we need to say memory-safe language. And if we're generous, we could include C-sharp or Swift in that definition. But that's currently not something that we do.
Matthias
00:24:19
Right. So is Let's Encrypt a corporate entity? Is that the business that funds all those projects? Or does Let's Encrypt also just belong to another organization? I only vaguely remember that they started in the Mozilla realm somewhere, but I don't know. I haven't followed up what they do now.
Folkert
00:24:39
Yeah, so the ISRG and Let's Encrypt, they're all not-for-profit businesses. Businesses, they run on effectively, or at least the projects that we do are basically, the money is ultimately from the big tech companies and the ISRG facilitates sort of putting that money to good use. So they do a lot of, I don't know, the convincing to someone at, you know, a major tech company like, hey, you have this budget that is, you know, left over or hey, you have this problem with this old piece of software that is not really maintained anymore. That might be a bit of security risk at this point. Why don't you fund us to create an implementation in Rust, a modern one, one that we can sort sort of guarantee will be maintained for the foreseeable future. And so they really play this facilitating role with regards to our projects. I think Let's Encrypt is something they run themselves. I'm not exactly sure what the funding model for that is, but it is still a not-for-profit sort of initiative that they set up.
Matthias
00:25:48
Let's focus on NTP for a moment. What is NTP? What does it stand for? When was it introduced? Give us some background on that.
Folkert
00:25:59
Right. So NTP stands for the Network Time Protocol, and it is a protocol for synchronizing time over the network. And in practice, the network generally means the Internet. So the network is an untrusted network that might span the globe. So this really has been sort of with us since the start of the internet. So it's like sort of around 40 years old at this point. Very old. It's gone through a number of iterations. So the initial versions, you know, have some flaws with the benefit of hindsight. So we're currently on version 4 of the NTP specification. And, you know, it runs on most devices, you know, around you that are connected to the internet. they will occasionally synchronize their time to the internet just to sort of be up to date and display the right time. So for most consumer devices, that is all there is to it. Occasionally, they sort of reach a server and ask like, hey, what is your time right now? And they will just adopt that time basically. But NTP is important for the internet because it is sort of crucial for a lot of the infrastructure that keeps the internet together. So in particular, like in a a data center, you actually want all your devices, all your machines to be sort of synchronized to one another with regards to, you know, when did a backup happen? You really want that timestamp to be accurate, right? Like if some error happens, for instance, you want to be able to interleave the logs of two machines and have that constitute a sort of an accurate global timeline. If the machines are not correctly synchronized, you could have a message that is received before it's even and send, and then good luck debugging that, right? Like that is awful. So just for sort of sanity, synchronization is very useful and very important in a sort of a data center or cloud sort of context. And then for security, you have this thing called TLS, right? The sort of foundation of HTTPS. And it relies on being able to expire certificates. These certificates just expire at a certain point in time. And if you were able to manipulate the clock of a machine somehow, you could trick it into using an expired certificate, and that basically breaks all of the whole security model of TLS. So NTP is important both sort of for infrastructure reasons, but also for security reasons. And that means that our implementation also needs to be very secure. You need that that implementation to be good right otherwise you'll run into issues i.
Matthias
00:28:37
Tried to remember when was the last time that i manually set the clock on my computer and it's probably been a decade by now is it also powered by ntp under the hood so i'm on mac os i don't know if it supports all the platforms or if different platforms use different protocols it's.
Folkert
00:28:58
It's generally the same protocol call, but most sort of consumer devices will use a thing called simple NTP, so SNTP. And that just does a simple call of like, hey, server at Apple or Microsoft or on Linux, there's this thing called the NTP pool. And that is a big pool of sort of servers that provide time for you. And it will just adopt whatever gets sent back. So for these sort of more serious applications in like a data data center, we actually want to have multiple sources of time because one might get compromised or there might be a power outage or whatever. And then you don't want to just desynchronize. And we also sort of more sort of accurately steer the clock at that point. So generally, what you can do is sort of jump forward or backwards. That is what a laptop will do. If I have a couple on a shelf here, and like if you boot them after three months, then the clock will actually be quite far behind. And then basically, it will just jump forward to the current time. But if you want really good accuracy, what we actually do is sort of speed up and slow down the clock slightly to sort of get its frequency to match the real frequency. And this is tricky because the frequency actually depends on like the temperature in the room, for instance. So there isn't one true frequency. It always depends on the current circumstances of that machine. sheet.
Matthias
00:30:30
It is true that when you haven't used your computer in a while or your smartphone that the clock might be off at some point i realize now that it is actually a problem that i also ran into and then it takes a moment for it to be up to date again i wonder if the hardware vendors sometimes do that on purpose even and integrate cheap hardware because they think that ntp or or SNTP will solve the problem. Yeah. I mean, technically it runs on billions of devices, right? So it's kind of a big market.
Folkert
00:31:02
In theory, of course, your computer could contain an atomic clock and you would never have this issue, right? But in practice, we don't because those things are expensive, they're heavy, and they are sort of, if you drop them, they generally don't work anymore. So the clocks in our actual devices are generally fine, but they will just drift. They will drift quite seriously, actually, if you have equipment that is accurate enough to measure that. So at our office, we actually have a GPS antenna that is a good source of sort of true time, the real accurate time in the absence of an atomic clock. We need to throw that thing out onto the street because our office turns out to be a Faraday cage at the GPS frequencies. So there's just this lone antenna in the street to give us an accurate time signal. And then we can actually measure, like on a Raspberry Pi, for instance, how much it drifts versus the true signal that we get. And it's very measurable very quickly. But of course, as a human, you would only really notice once it exceeds perhaps a couple of seconds, but certainly a minute, right? And that's... That takes a while before the desynchronization gets that bad.
Matthias
00:32:19
It's a very funny intersection between hardware and software because you could probably make hardware more accurate in order to improve your maybe accuracy in terms of time. But at the same time, you can throw software at it and solve the problem at the software level. But what i'm wondering about is if you wanted to improve the situation and you wanted to improve time synchronization with software looking at what ntp is and the implementation itself would you say that there could be improvements with regards to the protocol or would you say the main improvements come from fixing the software flaws that are in the existing implementations.
Folkert
00:33:05
So there are certainly improvements that can be made. This is in part what PTP was designed for. So PTP stands for Precision Time Protocol. It is more precision. And so generally, it uses some additional hardware features to get more accurate timestamps. But also, a lot of the sort of problem with the accuracy on the internet is the structure of the internet. So, in particular, you don't exactly know how far that signal needs to travel between you and the server that has the time. And that transmission delay and sort of variance in the transmission delay, because on the internet, sort of the path from A to B can be different from the return path from B back to A. It can go through different hardware, basically, different wires. The sort of variance that that introduces and the general noise, your time signal basically, determines how accurate your synchronization is. Now, it turns out that one of my colleagues is really into making better algorithms for that problem. So we believe that we have the best synchronization algorithm out there today. And so there's probably still further improvements that can be made algorithmically. But fundamentally you're sort of fighting against the noise in the network and so what ptp allows you to do is say okay i have 20 meters of fiberglass cable right here right between these two devices and you can program that in and then it will account for that and so long as that doesn't change you can sort of reduce the the noise in that way.
Matthias
00:34:44
Initially i thought oh why can't we just move from NTP to PTP, but it feels like that's not feasible because you need all of that information about the environment. You need to code that into the protocol to be more accurate. But it feels like what you touched on before, which is this better algorithm could be the way forward. And is that part of newer version of NTP or would you need a completely different implementation, completely different protocol to make that happen?
Folkert
00:35:19
Yeah, just to clarify, PTP assumes the network is trusted. And so it can take a couple of shortcuts because of that. NTP needs to sort of guard itself against potentially untrusted input becoming my wing. And so what it does is if you listen to multiple servers, it will pick the most reliable ones, the ones that agree with one another. So it needs to expend some extra effort basically to guard against malicious input. So what's sort of interesting about the NTP specification is, one, there is a specification. And in practice, this is mostly what our implementation is based on. It's not really based on the existing implementations, but we really started from from the ground up based on that specification. The specification does specify a synchronization algorithm, but it is sort of a public secret that that synchronization algorithm isn't very good. It may have been at the time, but certainly now we just know more about sort of how to deal with the noise in that signal that we can get more accurate synchronization. So the best implementations use a custom algorithm today And so technically, they're not spec compliant, but practically, they behave as if they are. They just give a more accurate time signal. We are looking at contributing towards NTPv5, so the next version of the standard. And in particular, something that we are trying to get in there is to not specify the synchronization algorithm. And to sort of leave that up to particular implementations, basically, such that innovation in that space can happen without sort of being technically not compliant with the spec.
Matthias
00:37:07
But these are all things that you solve on the protocol level, and you don't necessarily have to rewrite everything in Rust for that. Maybe, can you briefly touch on some of the flaws of the C implementation that made you think that Rust was a better case for this?
Folkert
00:37:25
Yeah, so like, ultimately, this project started because the ISRG said, like, okay, this is a fundamental building block of the internet, it is not memory safe, there have been some incidents in the past. And even though you know, it's like, if an attacker can know what time you have, that isn't so bad, but it's sort of a jumping board for further attacks, basically. So I think there are good reasons both to have modern implementations of NTP and then in particular for picking Rust for the implementation language. So we sort of touched on this before. When you're sort of looking at a 4NC code base, it is very hard to understand what happens. And for instance, around synchronization, and that is what this sort of software needs to do, it sends a bunch of messages over the network. So it is inherently sort of a threaded application. In C, that is very hard to understand. And so the existing implementations are quite old, cross-platform C codebases, and they have a hard time attracting new contributors for, I think, very obvious reasons. And so that also means that the existing pool of maintainers is very small and aging rapidly, basically. Basically so a lot of these folks have been around since the invention of the protocol itself and so they're absolutely close to if not past their retirement age and they might decide that they don't want to maintain this sort of software anymore and then the rest of the internet would have a problem so i think there's a lot to gain from a modern implementation where you just write in a language that has good tooling has good documentation and in general sort of makes that implementation more accessible to more people, basically, in terms of understanding what the code does. Then you think Rust is sort of an obvious choice at that point. We can guarantee that there are no memory safety problems. We get good performance. We use and Async to sort of handle all of that for us. So we don't need to do our own threading in the application at all. And of course rust has amazing tooling in terms of testing fuzzing fuzzing is very important because we are accepting untrusted input from the internet we want to just make sure that you know we don't run into panics or infinite loops or stuff like that, and also rust is nice to work with and in practice we see that we actually get a bunch of contributions to the code base because it is a rust code base so these are not contributions to to the algorithmic side of NTP, but more for instance, supporting the muscle lip C, or FreeBSD or something like that, much more around the edges where you just need a little bit of extra work to make our implementation run on those platforms. And this is work that anyone, any Rust programmer can do without intimate knowledge with the internals of NTP.
Matthias
00:40:26
I wonder how a setup of such a huge project looks like. I certainly see that there's a funding case for this, but then you still need to set up a project structure and have a timeline. And you mentioned testing and you also mentioned fuzzing. I wonder how you set that up, how you structure the project, how you go from the idea to the implementation to the rollout and so on. And maybe briefly for people that are not in the know, can you just quickly explain what fuzzing is as well?
Folkert
00:41:01
Yeah, let's talk about fuzzing. So fuzzing is the idea that you throw random, in quotes, random input at some program. And verify that it behaves correctly. And here correctly doesn't necessarily mean that it gives the right output because your input might basically be invalid input. But because we effectively listen for messages from the internet, a, well, some sort of error or a malicious attacker can send any sequence of bytes our way. And we just need to make sure that our program remains operational even if we get some weird sequence of bytes as our input. So we want to make sure that we don't crash, but we handle these errors gracefully, and also that we don't run into other sort of weird error conditions where we might enter an infinite loop or just use a lot of resources that could be used to do some sort of denial of service attack. We use fuzzing for a lot of our parsing logic, for instance, and then also in several other cases, just to verify the correctness of certain data structures, for instance.
Matthias
00:42:15
Do you have any hard guarantees around the implementation itself that it doesn't produce any errors? Is that written down somewhere? And in general, how is the testing infrastructure and the general project infrastructure for this sort of endeavor? ever.
Folkert
00:42:32
So in Rust, it's really hard to guarantee that you have no panics in your code. There are also certain cases where we do want to panic, and this really makes things tricky. There are some very hacky ways to guarantee that a Rust program doesn't panic. You need to do some linker stuff. But we do actually want to panic in certain cases where we recognize that the signal that we get is so messy that we can't make a good decision about how to synchronize. And then we basically decide, Like we're just going to stop right here instead of going in a wrong direction. We're assuming that sort of having the clock go on as it currently is, is better than us sort of moving at very radically or potentially in a sort of incorrect direction. So that makes it really tricky to guarantee that we have no panics. And this is why the fuzzing is so important to just sort of make sure that for any input that you give our program, it will not hit a panic. Right and in Rust a lot of these panics are sort of hidden in like some sort of slice index or, An unwrap somewhere is very easy to write sometimes, but we try to be very careful, basically. This is unfortunately sort of a... Well, the fuzzing really helps, but ultimately we also just need to be careful. We need to be sort of dedicated to not write unwraps and other panics that can happen in a real program code.
Matthias
00:44:02
If i was a critic i would wonder if i couldn't do the same thing in a different code base for example with the existing c code base i could also run the fuzzer and i could say this is just blind rewriting rust enthusiasm around this project what do you think about this how would you answer to such criticism yeah.
Folkert
00:44:26
So like it's it's important to emphasize here that like this this is not a straight-up port of an existing C program. There are some other projects where that does happen and where it actually still makes sense to do that rewrite in Rust. But we are really just a new implementation, a from-the-ground-up new implementation because for this sort of project, we believed and still believe that we could do better in terms of architecture architecture or create a more idiomatic Rust program by just starting from scratch based on the specification. I think there are still guarantees that you can give about a Rust program that you cannot reasonably give about a C program. Of course, a lot of the fuzzing tooling does help. Problems still occur, right? CVEs, like critical security vulnerabilities, still happen in C codebases because it's very easy to miss something, actually. In fact, we had one sort of mishap in our codebase where some part of the code just didn't get fuzzed. And of course, there was some out-of-bounds access in there. And it would panic if you send it an incorrect input stream. So you still need to actually be very careful even with that tooling. I think the Rust compiler just gives us so much baseline security that we have less to worry about because out-of-bounds access will at least panic, right? It won't silently continue to run. It will actually sort of fail loudly. But then also, I think Rust has all of these other benefits of, it is a modern language, it has good documentation, it has a good beginner experience, experience we can onboard new people onto the project easily so besides like all of the people sort of working on the implementation right now are sort of you know n20s early 30s so we have we have a bunch of decades that we can remain active on this project and hopefully we can sort of keep that going but.
Matthias
00:46:35
Shouldn't you get an error if you do something strange with with an input for For example, I imagine that, let's say you want to convert it to UTF-8 or so, you would assume to get an error message that you can handle. Or why would you get a panic in such a case? Is it because of the parsing logic?
Folkert
00:46:56
Yeah, it's generally parsing is sort of where we have untrusted input, basically. Yeah. And there, you sometimes just write a straight up index, say, OK, I want the 11th element out of this slice. And then if you give an invalid input to that function that does this slice access, then you can run into issues there. This is, of course, rare because you do, or at least we test. We have a very extensive test suite, which means that in practice, the size of our code base is roughly comparable to the C code base, but they generally seem to have way less in terms of testing of the actual individual bits of logic. So of course, they will actually run the implementation and sort of verify that it runs correctly in the wild. But we have much more atomic, basically, tests of just the parsing logic or of just the sort of spawning logic or synchronization logic, et cetera.
Matthias
00:47:59
Were you able to use any existing crates for the parsing logic, or did you have to write that yourself?
Folkert
00:48:06
So that is interesting, and this is true for a bunch of these projects, because they are... Well, basically, we want to have very few dependencies for a couple of reasons. First of all, generally, the logic that we have is quite simple, and so we can just write out the actual logic. Like this is not complicated parsing it's really you get 48 bytes they all mean something you need to sort of pull that apart so we can write that without some sort of specialized parser crate, we originally had a bunch of additional dependencies like clap for the command line interface and there's a couple others in there that ultimately we've decided to remove move, both because of simplicity, reducing binary size, and also just supply chain attack risk ultimately. Also, build times get better when you do this sort of thing. So over time, we got quite hardcore about eliminating dependencies. We still have a bunch. I think it's like around 100 total if you walk the tree of dependencies, basically. This is mostly like tokio and rust-tls, I think, are the two big parts of that tree. But yeah no we we try to be actually quite hardcore about doing the low-level stuff ourselves because we we're just i don't know more more lean more agile we can actually just write that out and have full control over exactly what happens how errors are handled we don't want some extra layer in between that might obscure what really goes on or introduces inefficiencies.
Matthias
00:49:47
Tokio is kind of a huge crate, or it has at least many sub crates. What do you use it for exactly?
Folkert
00:49:56
Yeah, so the architecture of this project is that we have this sort of pure core of the program that implements all the logic. So this is parsing logic. This is sort of there is a state machine in there that decides what to do with the clock. This is very testable logic that, you know, in theory, this would run on sort of any device. Like it doesn't depend on any operating system capabilities. And then we have the sort of outside layer that does all of the input output. And because we want to maintain connections with multiple servers, there's a bunch of timers in there for when to ask them next for what their time is. We need to keep connections going without blocking the whole program, basically. If we synchronize with three different servers, we want to run those requests concurrently, but actually in parallel so that, well, that is just essential for how this program works.
Matthias
00:50:56
It means the core itself is synchronous and on the outer layer, you have an async wrapper around it. Okay, that makes sense.
Folkert
00:51:07
Yeah, and that means like the core is very testable and very portable. tokio tokio tokio tokio tokio tokio tokio tokio tokio tokio tokio tokio tokio And then the outside layer, because of tokio, is actually also quite portable. There is some stuff at the very bottom with regards to actually updating the clock and with regards to configuring sockets to sort of capture timestamps that we need. And that is the sort of operating system specific bit that needs custom work for Linux or Mac or FreeBSD.
Matthias
00:51:36
With regards to that, do you sometimes wish that the ecosystem, was a bit more granular or maybe the entire async Rust ecosystem was a bit more granular and you could mix and match things and there were clean interfaces between those dependencies? Whereas nowadays you have as a dependency and you pretty much buy into the entire ecosystem there. Would it help you in your situation or doesn't it really matter that much after all? And maybe the coherence of the entire ecosystem is also an advantage.
Folkert
00:52:13
Yeah, for sure. I think for us, it really helps that we don't have to think about threading a whole lot. It's just like will take care of it. We are careful with limiting through feature flags sort of what gets pulled in. It's still quite a lot, but I think all of those parts make sense and are a kind of complexity that we don't really want to bother with. is a sort of well-regarded dependency, so in terms of supply chain risk, this is just a risk that we're sort of willing to take. Also we don't upgrade immediately, so if something does happen, we will have some time to sort we can just not upgrade to newer versions. Right? So we're generally okay with that. I think just makes a lot of sense and it provides us with a lot of things that we just need and wouldn't want to do ourselves. There are some cases where actually is a limitation because we're doing such low-level and sort of niche things. But we actually got some stuff upstreamed into , which is really nice. So these days, it's always fun, right, if you can sort of throw some of your unsafe code into a dependency and have it not be your maintenance burden anymore. So that's worked out. And hey, right, everyone can use it now. So that's been really fun. And also a sort of a cool part of the structure of this project is that we have time to contribute back to the dependencies that we use. or for libc or whatever else And so when that makes sense, either for might come up in the future, we're very happy to do that. And in general, these projects are very receptive and sort of active. So that's been a good experience. That m
Matthias
00:54:00
eans you put through the test and you maybe check it at the boundary of what it's capable of. I imagine that it's one of the areas that were not really well tested or maybe you don't have that many use cases at such critical pieces of infrastructure. And probably you run into problems that they haven't even encountered yet or even thought about.
Folkert
00:54:27
Yeah, we were just doing such low-level things that the API support just wasn't quite there. And so again, this is kind of cool, right? That like in Rust, actually work though, right? we can go to source and just learn about like, okay, how does And how can I make this, how can I implement this feature? Because I didn't really know much about the internals of async before this project. And then now I know at least something about the internals. There's a lot to go through, but, I think that is really nice that you aren't limited by the current state of your dependencies, even the current state of things. You can actually go in there and sort of make it work yourself. And at first, we did this just internally in our code base. We can take some shortcuts. But of course, ultimately, the right solution, the more robust solution also, . is to integrate it into, in this case,
Matthias
00:55:23
Looks like you made great progress here. and it also feels like you're almost done with the implementation or the protocol, the implementation itself is quite sophisticated. If I wanted to play around with what you've built so far, how could I get started? And what's next for the project?
Folkert
00:55:40
Yeah, so basically the implementation of the protocol is complete. We are working on some improvements to the protocol. So this is the next version of NTP. I believe we are the only version that implements the current draft. It's behind a feature flag. Don't worry. It won't actually make it into release binaries, but we can play around with it. We're also looking at making it easier to use a thing called NTS, which basically uses TLS to establish connections. So you get a bit of extra security on your connection that, or at least you know that that you get your time from a reliable server. So we've had a 1.0 release last fall. And so you can go and download installers. We have nice installers for Linux anyway. And this installer will also do a bunch of setup because you need some stuff in certain places. On other platforms, you need to build from source and you then need to set that up. But that will also work on FreeBSD and macOS because these are so close that it was just so easy to support them that we might as well, basically.
Matthias
00:57:00
What's the name of the package?
Folkert
00:57:02
It's called ntpd-rs is the name of the repository. repository. I believe also the package in the sense that we are taking steps towards getting included in common package repositories, for instance, Debian and Fedora. That takes a while to get your project in there, but we're sort of on that path and collaborating with maintainers there to make that happen. It takes a while in general. And then also because we are a Rust project, there are just some extra setup things that need to happen and take a bit of time. I think in In general, that process is pretty good these days, but it's still kind of different from what they're used to. And so there is a little bit of extra work that needs to happen.
Matthias
00:57:45
Well i And at some point, will I be able to say apt-get install ntpd and it will pull the Rust version?
Folkert
00:57:52
t would be apt-install ntpd-rs, or ntpd-rs, because it can't overlap with the existing implementation. But that is the plan, yes. Yes, like ultimately, we would like it to be that easy to install. It would also be really cool if certain Linux distributions pick it as the default NTP synchronization mechanism. Because even in the sort of simple case for just a consumer device, we can actually be more accurate with little extra resources used. So hopefully that'll happen at some point.
Matthias
00:58:29
And then will I be safer? Will it be more secure? Are you doing certain things to make the setup as secure as possible for me? Do I get a lot of benefits from the Rust implementation all of a sudden?
Folkert
00:58:41
Mostly you just get that warm, fuzzy feeling of running Rust software. I think on a personal machine, it's just nice to be running modern software. I don't think the impact is that big. The impact is never that big for, I think, your consumer individual device. The real benefit is when you sort of deploy this at scale. And so this is actually a sort of an interesting aspect of the project. It's like, you know, we've made this new fancy implementation. We're very happy with it. As far as we know, we have the best accuracy. You know, we have good performance. The binary isn't like excessively large or anything. thing. So we're doing good along all of these sort of technical dimensions. And so like you hope that such a thing would sell itself. And this turns out to like sort of not be the case. Certainly rewriting in Rust alone is not enough. People just will not upgrade if their existing setup is sort of fine. They just won't touch it. Right. So that is why we already invest heavily in making sure we have good documentation, that we have good observability, that, you know, Our performance is at least on par, but hopefully better, than existing solutions. We need to put in a lot of extra work to actually get existing users to move. In particular, because NTP is something that you forget about, I assume most people don't know about it really, even though it is this sort of vital underpinning of the internet, it's very unknown. And once you have a good setup that doesn't cause you issues, why would you switch? And so we really need to put in a lot of work where at this point, I would say, at least for the outside world, that it's implemented in Rust should be an implementation detail. What you get is actually a very mature and modern implementation of this crucial dependency. And yeah, that's sort of the approach we're going with. Like even without the Rust part, This implementation should be able to convince users that generally don't care about what language their software is written in.
Matthias
01:00:52
Now I have this small part written in Rust. Can I grow from within and rewrite other parts? Does it make sense to spread out? And are there any other protocols that you see might be a great fit for Rust in the same realm?
Folkert
01:01:08
Yeah, so this is sort of interesting, I think, in terms of how do these projects happen. So I mentioned that NTP was originally an ISRG project. So they, I think, funded it for about a year, but the project's been going for two years because we got a big grant from the Sovereign Tech Fund to continue development, basically. And so even though the money isn't sort of abundant necessarily, I think it is less of a blocker for this sort of project at this point. Main blockers are, you need to actually do the work, right? Like NTP is niche. So like how many people really exist in the world that both know Rust and then are excited about this domain and want to improve it. So you need to have a lot of domain knowledge and then also the Rust knowledge for, you know, the technical knowledge to sort of make a modern implementation. Funding still requires work, but it is available. It is there. Then the major issue at this point for us anyway is just how do we create that adoption? How do we facilitate that? Because, I mean, that is ultimately sort of crucial to making this project really successful, right? We think that sort of along technical dimensions, it is very successful. But in terms of real-world impact, you actually also need a lot of real-world use.
Matthias
01:02:30
Yes, and I can imagine that if you do that again, you repeat the same process for a different protocol, you can reuse a lot of what you built with regards to packaging and testing and the entire infrastructure that you have now is a bit of a template for future projects. Do you agree? For.
Folkert
01:02:48
Sure. Yeah. There's also a lot of, so there's a lot of infrastructure that you can reuse. Absolutely. So packaging is one, I think also sort of performance benchmarks are hopefully another one that we can sort of standardize on soon, but a lot of the knowledge is actually much more intangible. I would like, how do you get something accepted into the Debian package repository? For instance, how do you reduce dependencies and how sort of, dedicated should you be in that regard. So there's a lot of experience that you get from running a project like this. And actually, we've run a couple. We will run more in the future where you also see it's much easier to get up to speed at this point. And certainly also something that you learn is how to acquire the funding. Once you've done that once, it's relatively easy to replicate. You may not always end up getting the demands that you want, but at least the process is now pretty clear.
Matthias
01:03:51
What is the process?
Folkert
01:03:54
Well, so for the ISRG is they have their own agenda of our sort of list of projects that they want to do. If they can find the funding for it, they will sort of pick an implementation partner. So then it really matters that you sort of show that you have the technical competence for that particular subject. So for a lot of the timing-related projects, we have that experience now. For other domains, you know, you have to show that you do or that you can acquire that knowledge sort of along the way, right? For the Sovereign Tech Fund, and like there are initiatives in a lot of European countries for something similar, you just have to write a good proposal. And really what that comes down to is sort of, making the sort of public benefit of it sort of obvious. So for some projects that is easier than for others, but generally you can make that story work for most of these foundational projects.
Matthias
01:04:57
Can you imagine any critical software right now that we use on a day-to-day that is severely underfunded, where you say there's definitely a case for this, we need to have more eyeballs, we need to have better funding?
Folkert
01:05:14
So I think sort of paradoxically, a lot of this software isn't actually that obvious. I worked on the Dutch system for emergency response messages, basically, the sort of a system that will wake up someone because there is some sort of accident and they need to get there. So luckily, this is now modernized, right? But for a couple of years, the state of that software was pretty bad. Like it was just some big binary blob that the source code wasn't available. It ran fine, as far as I know, there were never any incidents, but like that is not a good situation to have for something that crucial. And you wouldn't really know about this because because that is not public knowledge, right? But there's a lot of software like that in a lot of critical domains, like energy, shipping, any form of transport. A lot of these very vital sectors have a lot of this very dated software on very dated hardware. That is a major issue, a major risk. With regards to the internet, I know there's currently work on, well, TLS is still continuing with Russell's, of course. There is work on DNS. I think that covers most of the major protocols for sort of the public internet, at least. And there is now some branching out. So you can sort of see sudo was also an ISRG project. Their connection to the internet is already much less obvious, but sudo is in the chain of running internet infrastructure. And so ISRG still thought that that would be sort of in scope and sort of a useful project to do. There is now sort of a focus more on media decoders. So you may remember this recent problem on Apple devices, I think, with the font decoding. There was a pretty serious bug. This happens every so often. So either the image decoding or font decoding or something like that will have a critical security vulnerability. Right. And so that is the next sort of domain where, at least sort of internet related, where the memory safety will have, should have a huge benefit. But there, the performance is much more crucial, right? Like you can't trade safety for performance there. You need to maintain the performance and then also somehow guarantee the security. So that is quite a tricky sort of engineering project where you want to limit the amount of unsafe Rust code, but also you need some amount of unsafe Rust code just to make sure that the performance is there.
Matthias
01:07:53
It's funny to think how a lot of our public infrastructure runs on very deprecated legacy software.
Folkert
01:08:03
Yeah, don't think about that.
Matthias
01:08:05
There was this story on Hacker News the other day about some German train provider looking for a Windows 3.11 admin. I saw that. Maybe in this context, it might even make sense. I don't know all the details, but for sure, there's a lot of legacy out there and a lot of deprecated things. And people don't know how it works. Maybe they don't even have the source code, as you mentioned. So it is true. true so there might be future projects that are worth funding.
Folkert
01:08:38
Yeah there's this this uh there's this fun story about the dutch tax collector's office that basically can't collect tax anymore because they don't have any cobalt programmers this is a major issue where all of that stuff still runs on these very old mainframes and they just have a really hard time modernizing that infrastructure where it now impacts the actual performance of sort of their their core business as it were. This problem is everywhere.
Matthias
01:09:07
I wonder if that's a bug or a feature, but that's the topic of another. No, n
Folkert
01:09:12
o, no. Like everyone agrees it is a bug because in effect, they can't make any changes to tax policy because they just can't get the computer systems to go along with that. So yeah, it's a big problem. It's the most obvious one too, right? Like there's a lot of stuff that is, of course, hidden under the surface, but that would benefit hugely from having a more modern software and hardware, really.
Matthias
01:09:38
Time flies. We're already getting towards the end. There's a tradition around here to ask a last final question. That is, what would be your message to the Rust community?
Folkert
01:09:52
Yeah, so I have this, I don't know if it's a thing, but I am very passionate about compile time performance. performance. And I think we should be more serious at, first of all, admitting that this is a problem. Of course, the performance is better than C++, but doing better than C++ is not hard and shouldn't be our benchmark. Realistically, for all the projects that I do, compile times slow us down. They are a major source of frustration and just lost, productivity. And again, like any sort of even small improvement here has sort of majorly sort of outsized benefits to all Rust programmers in the world, which I believe is like over a million or over 2 million now.
Matthias
01:10:37
3.7 million as far as I'm aware. So y
Folkert
01:10:40
ou know, even one second saved, right, per day translates into huge benefits. I think we can do much better than that. There are very cool existing initiatives in that direction, like the parallel frontend, etc. But I think having more of that focus is important. Because of course, we all have our favorite unstable feature, right, that we would like to see stabilized. But the performance of the compiler just hits you every time you hit compile or test or check or whatever. And like, that is no joke, like we need to really, I think, do better there collectively. And then separately, I would like to say something about the RustNL conference that we will be organizing the 7th and 8th of May this year in Delft, which is reasonably close to Amsterdam. For everyone from outside of the Netherlands, it's very, very close. And so that was a lot of fun last year. We had a lot of, well, both national and sort of international speakers. speakers and it was just a great time and we hope to replicate that this year.
Matthias
01:11:48
If you're in the area come by i will most likely be there and i hope to see a lot of people it was so much fun to talk to you today i learned a lot about protocols so thanks a lot for all the input it was amazing, and i hope to see you in person at some point in time awesome thanks. Rust in Production is a podcast by corrode and hosted by me, Matthias Endler. For show notes, transcripts, and to learn more about how I can help your company make the most of Rust, visit corrode.dev. Thanks for listening to Rust in Production.