 Hi everyone. Thanks for attending this talk and namaste to all the people in India. I'm Daniel Paulus, director of engineering at the company with the probably cutest logo of them all. So we like raccoons as you can see it check me. And we're going to talk a little about how we can use Go iOS, my open source project to run real device testing with Appium on Linux. If you look at schedule basically, so we'll first briefly talk about what actually happens when you install an application with Xcode like under the hood. This is important to understand the rest of the talk and I try to make it not super technical. And then we'll talk about so how is it possible that we can do this on Linux while these devices actually should only work on macOS. And then the second part of the talk will be talking about deep proxy, but more about that later. So how did this whole project come to life? Why does somebody spend so much time on something like this? So I always enjoyed doing a little reverse engineering on the site, right? But I'm by no means a security researcher or an expert. But I when I was a kid, I wrote some cracks and key generators and, you know, not then I do some captured attack puzzles. And then I worked at a company called Sauce Labs for over two years, which my quite a few of you white low. And I worked as a senior engineer and we did a lot of reverse engineering for iOS specifically and also sometimes for Android to make real devices or to expose features real devices had to customers. And I basically back then started go iOS as a fun project to learn going and just did some, you know, copycat commands from the by mobile device, which is the other very important open source tool that you can use to control iOS devices on Linux, but it is missing one critical thing. And we'll talk about this also later. And at some point, after leaving Sauce Labs, I decided to develop go iOS into a full blown iOS CLI tool. I also have one other project that allows you to do screen recording for iOS devices on Linux, similar to what QuickTime does on the Mac. But we'll see this only briefly in a demo today. So what does the Xcode actually do under the hood? When you install something like WebDriver agent and then start testing it, right? So basically the foundation of every, every real device, Appium iOS device tests. Right. So you need to start WebDriver agent. So let's look at what actually happens in detail. I created this little drawing. And I hope it helps to understand the process from a not so super technical perspective. Right. So if you look at what you can see is Xcode as an example for Mac OS tools. And you can see a Mac and you can see an iOS device. Right. And then you can see two services. The first one that is running on your local machine when you have a Mac book or any kind of Mac is the so-called USB Max-T service, which I think is short for USB multiplexing daemon. And what that actually means is basically you can kind of imagine this as a server that is running on your Mac all the time. And it awaits connections from Mac OS tools. And then the second service you can see on the iOS device is the so-called lockdown service, which is running on a predefined TCP port. And again, this is really just you can imagine this as a server. Right. So nothing super complicated. And then so let's go to the next step. So the first step of every real device interaction from Mac OS is always a connection to Varan USB Max-T. Right. So for those of you who are using Windows or are not super familiar with networking on Linux and Mac OS, you have so-called Unix domain sockets, which basically means it's a regular network connection, like with a TCP port, but you can use a file name as an address. Right. So on Windows, for example, I think this would be localhost 15,000 running on port 15,000. So every Mac OS tool when it wants to interact with devices at first connects basically to this address, which is like an ordinary network address. Right. Establishes a connection to the USB multiplexing demon. And then something interesting happens. You can see that the USB multiplexing demon, what it basically does is it allows you to connect to services running on iOS devices with a regular network connection. Right. So there's nothing USB related. You don't have to know about USB coding or anything like that. You can really just send regular messages over a network and something like a TCP connection. Right. And in case of app installation, what basically happens is that Xcode sends a command directly to Lockdown that says, hey, Lockdown, can you please start the application installation service? And then the Lockdown service responds and says, yeah, sure. I just started it. Right. And now you can see a second service spawned on the phone. And Lockdown tells you on which network port it started. Right. So Lockdown basically says, yep, started the service runs on 4321, which is a number that I completely made up. So those are random ports. And now Xcode knows where the installation service is running. So the next step is Xcode again connecting to the demon. And basically this time it says, please connect me to TCP port 4321. And then it gets a connection, a regular network connection that is piped through to the app installation service, which is called streaming zip conduit. And now Xcode is basically good to go and install its application. Right. And I think this is a good example, because this is basically how every interaction works. Right. If you have something else running here, so not Xcode, but maybe the Apple configurator or the accessibility inspector or any tool really that interfaces with iOS devices through the USB cable, they all go the same way. Right. So they connect to the USB Max Deep. They connect to Lockdown. They ask Lockdown to start a specific service that they use. Lockdown tells them the port. And then they start communicating with their service. And if you look at the summary, so streaming zip conduit is a service that Xcode uses to install applications nowadays. And if you're curious about what the actual communication looks like, that Xcode does here. So most of the communication is using standard Apple property lists. If you are not familiar with those, that is basically just XML format that contains serialized dictionaries and arrays. Right. So the first thing that is sent, Apple sends such a property list with a command that says init transfer. And you can actually see this in plain text when you look at what's going over the wire. And then all the individual files of your application are sent in a pretty strange zip-like format, but they're all uncompressed. So this is like typical Apple stuff. When I implemented this for Go iOS, basically the first thing I tried was using the native zip implementation that Golang has. But of course, the Apple zip implementation was not compatible with that. So I had to kind of write my own little custom zip format. Luckily, all the files are uncompressed. So it's not that hard actually. And interestingly, what you can see is that of course, when you think of the Xcode progress bar, every file is basically sent individually. So then Xcode knows when files are sent, when the device is received them. And once all the files of your application are sent over to the device over this regular network connection, you receive another XML PList with installation progress. So it actually sends you the phone, sends you back percentage values, how far along the installation process is going. And this is what ends up in the blue progress bar in Xcode. So I think this is actually really cool to see this and understand what's actually going on under the hood. And once the install progress update with status data complete comes, Xcode basically knows, okay, this is done. So let's look at, so what can we conclude from this? So I think it's important to understand a few things, right? Every tool and service interacting with iOS devices from a Mac over the USB cable basically works exactly like this. They all use different services that they start on the phone, but they all use lockdown and they all use similar protocols. And now the cool thing is we can figure out how these protocols work. We can implement them. Sometimes we can make them even more stable than the original. So I remember a specific instance when we implemented gathering CPU metrics and memory stats for applications. The original Xcode implementation was very fragile and you could basically kill the whole service if you didn't stop it before you closed Xcode. And when we look at how these things work in detail under the hood, we can actually make them more stable than the original sometimes. And of course, another conclusion is we can do all of this in Linux, right? Because it's just using standard networking stuff, SSL, XML, some remote procedure calls. So nothing that would force us to use MacOS. We can go ahead and do this. But of course, we need some tooling for this, right? Because doing this just like that is going to take a lot of time. So this is why I wrote Go iOS. It was inspired by the mobile device, which does something similar but is written in C. And I think GoLang is a little easier to learn and understand than C. Probably a lot of C developers are hating me right now for this comment. Sorry. And Go iOS right now is in a state where it can do most of what liberal mobile device can do. But it can do more than that. Particularly, it can launch XC tests, which is important for Appium, right? Because this is what Appium for iOS real devices basically is. It's using WebDriver agent, which is a wrapper around the XC UI test framework. And you can launch apps with it, kill apps, and much more. And before we go on, let's look at a few design considerations that I made when I implemented Go iOS. So one thing that always annoyed me about... There's actually a question from Shamal Kumar Surya. So the question is how to put in background and killing it from background. How to put apps into the background? Yeah, I think that's what he meant. Do you want him to explain his question? I can just say how you put apps in the background. And then if it's not that, maybe can ask a follow up. Actually, I think one thing that you could do, there is an issue on the Go iOS repo also about something similar, which was about waking up the device. I think one thing that you could maybe try is using, is launching Springboard. It's a bit hacky, but I think it would work, right? So if you launch ComApple Springboard, I guess it would go to the foreground and then whatever app you have would go to the background. Could work. That's the best answer I have right now. But if there's a way, then we can actually find it and we will see later in the talk how it works. And so the CLI is JSON first, right? For easy parsing, because this is something that always annoyed me about other tools. You have to do some weird Rekix string parsing to get command line output. And with Go iOS, you would see that everything comes as a JSON object, which is pretty cool, especially for Node.js JavaScript developers. It uses doc opt command line interface syntax. So everything comes in one command. You don't have to have tons of tools lying around. It's a modular build, right? So you don't have to use the CLI if you don't want to. You can also just directly import the modules and write your own iOS web servers if you want to control stuff over HTTP rather than using the CLI. Very fast builds. It's natively compiled because written in go links. So it's a very small set of tools that are running pretty quickly. And then it comes with a powerful debug proxy tool, which we will talk about later. And last but not least, it's MIT license. So if you want to build cool stuff with it, like, I don't know, automated iOS device configured Raspberry Pies? Well, you can. And I'm always happy to help. So I'm pretty active on my issues page if you have any questions. And then let's quickly look at what we can actually do. And we'll just pick out some of the commands. You see that there's a list command that will list devices. There's the info command, which is the equivalent to iDeviceInfo. There's an image command to mount developer images, which is a super technical thing that maybe not that interesting. You can grab the logs, make screenshots and a few other things. I think most notably are one thing that I added recently, which could be pretty cool for people using Appium. It now has the command. I implemented this device date command. So if you've noticed in recent version of Xcode, Apple introduced a way to set like three simulated 2G connections or put the device into different thermal states or have degraded GPU performance and these kind of things. And Go iOS actually allows you to do this from the command line. So you don't have to, I don't know, write some weird Apple script for Xcode or do something else. You can just use Go iOS and also enable all these things that Xcode allows you to do. And then let's talk about, so what else can we do? And the answer is everything. Go iOS includes a convenient implementation of DTX. I called it the holy grail of iOS remote procedure calls. If you don't know what all of this means, it's not super important. And I don't think we want to talk about DTX here. I think the important takeaway is that DTX has been reverse engineered partially by many people. And there's pieces lying around on the internet. But what we were missing was a good implementation of the protocol. So you can use all the interesting iOS services, right? Because this protocol is used for launching tests, launching apps, killing apps, grabbing CPU metrics, using accessibility APIs. So for all of these cool things, there was no way to do it prior. And Go iOS comes with a really nice implementation. So you don't even have to understand how the whole thing works. You can just implement your own features if you want. And so we did talk about containers, right? So where are the containers? I created a demo project to work with iOS devices in a containerized Linux environment. With this one, you can easily set up your local iOS device rack on a cheap Linux box and run Appium there, right? So the cool thing is that all of this requires hardly any CPU resources or anything. So you can run like 30 devices or so on a Linux box easily. We are about to watch a small demo of all of this. And if you want to follow along later or try it yourself, there's a medium article explaining all these things in detail again. And all the source of the demo that you're about to see is also on an open source repository. So you can get all of this going yourself. And if you encounter any issues, right? I mean, I guess most of you are quality assurance, right? So you know the problem when developers develop something. And in my case, I know pretty well how all of this works. So it's always hard to write documentation that is understandable for people who are not as familiar with the technology as I am. So if you have any troubles getting started or if I missed something that for me is trivial, but for someone who's not into that is actually a problem, just create an issue in GitHub and I'm super happy to help an update. But yeah, now let's look at the demo. So this one is prerecorded, right? So and you can see that I checked out the iOS Appium on Linux repository. And what I'm doing is building a Docker image that contains Appium and contains Go iOS, the latest release of it. So the Docker image basically downloads it, unzips it, and adds a script to run Appium. And all right, let's copy the container, the image ID and just run it, right? And all these scripts are contained in the repository, right? And here you can see basically we have started Appium inside the container right now, right? So welcome to Appium 120. Okay, here's the container. And now let's run Web Drive agent, the foundation of Appium real device testing. And these files, right? I'm using Docker exec to run the command inside the container. And I named the Go iOS binary iOS in this case. So it's the same binary. And here you can see, right? So the command is run WDA. And that's all you need to do. So basically Go iOS runs a pre-installed Web Drive agent on the device. And you can see that all the logging output is in JSON. And you can actually see some of the method calls that are usually made by Xcode, right? So Go iOS has a pretty generous log level. And you can see a lot of the outputs, which is pretty interesting if you want to understand, you know, how does all of this testing flow actually work? And then you will see when the phone tells Xcode, when something is done and sends events back and forth and all that. It's actually really interesting, but too much stuff to go into detail right now. And you can see Web Drive agent is running, right? Awesome. So we just started Web Drive agent on Linux. Really cool. No macOS involved. And now let's, to prove that Appio was actually working, I wrote a little Web Driver IO test. Shout out to my friend, former co-worker Christian Robin for this amazing framework. As you can see, I wrote a very simple test that really just opens the YouTube video, waits for five seconds and then opens the weather app, right? So nothing too fancy for this demo. Oh yeah. And then on a side note, I'm using also my reverse engineered other project, the QuickTime video hack to see the actual real device screen on the Nooks, right? So this is what you could do usually with QuickTime on the Mac. And here you can just do it with my tool. We actually have some more questions, Daniel. Okay. Yeah. So we just finished the demo quickly and then any other questions? Sure. It's just like a minute or so, right? So we run it and then you can see, right? So I ran Node.js on my local machine and then you can see inside the Docker container Appium is starting to create its session. Oops. And you can see things are happening here in Web Drive agent and suddenly boom, action, right? So Web Drive IO is now opening YouTube and to prove that it's actually a real device, not something emulated, you cannot see this here, but I actually tapped on the play button physically. And, right? So it opens the weather app and we're done. Nice weather in Lyon. Okay. We can proceed to the questions. That concludes the demo basically. Okay. Yeah. So someone is asking if it is possible to use this tool on Linux to communicate with devices via network? Yes. I mean, it depends on what you mean by communicating. If it's about using devices remotely, then yeah, that's totally possible, right? So you can basically do all of these things from a remote host as well. So if you want to interact with devices directly over a network, that is totally possible. Okay. The other question is, does it work with simulators or only real device in Linux? It only works with real devices. Simulators work a little different. They're not really devices per se. They're just Mac OS applications. So porting those to Linux is an entirely different story, sadly. Okay. Now, the other question is, will this be also supported on APM 2.x? So it will work for sure, right? Because like I said, it's a web driver agent is and will stay the foundation of iOS real device testing for APM indefinitely. And yes, it will be supported for sure. Okay. Another one, have you modified the WDA? No, it's default web drive agent, no modifications. In fact, you can run any XT test, right? So it doesn't have to be web drive agent. If you're only using XT UI test or you're using XT UI test in addition to APM, that's also perfectly possible. I see. Someone's also asking, can you run this on mobile apps? Oh, I don't know. I've never tried this. Okay. What means the almost Mac freeway? So one thing that you cannot do on Linux is actually compile and package your application, right? So this is beyond the scope of Go iOS. Because Xcode basically is like super proprietary and this whole compilation and code signing process usually does not make sense to reverse engineer. So for that you need a Mac. Okay. We actually have quite a few, we have about 10 questions. Do you want to continue your presentation first and then get back to the questions? Yeah. Makes sense. Also, I think we'll have quite some time. So let's see. So of course, one question that some of you might have is, so how can we make this future proof, right? I mean, this is reverse engineer. Doesn't that mean that every iOS release kind of breaks everything and then it doesn't work anymore? Kind of. So usually with every iOS release, Apple changes something, right? And then you have to make some modifications or some changes to make it work. So that's a bummer. But the positive thing is for once that the protocols that are used underneath, so this whole P list, property list stuff and the DTX protocol, they are relatively stable. So there have been no major, I think not even no major, I think there have been no changes at all since the beginning of iOS for those. Right. So you can see it in my mobile device. They also say this since iOS 9, nothing has changed. So basically the only thing that you need to do is similar to using private APIs, right? So if they rename something or they refactor something, change the way that you need to call methods, you'll have to make these adjustments. So that's good. And then what else? Reverse engineering is hard, right? We don't want to decompile binaries or look at header files. It's way too complicated to do this every time. It's slow and inefficient. And really, nobody wants to look at binary files. So ladies and gentlemen and dear non-binary friends, which is the only pun I'm making in this whole presentation, I present to you a deep proxy. Deep proxy basically solves this problem for us. And to understand deep proxy, let's look at this image again, right? So you might remember this one from before. And now of course, someone could come up with this idea quite easily, right? So the thing is, if all of this is simple network communication, why don't we use something like Wireshark to just check what's going on the wire. And so with Unix, the main sockets, it's not as easy as with TCP ports, but still totally doable. And I did not use Wireshark. I just implemented my own tool. And so the idea is basically, as you can see that what deep proxy does under the hood is it's basically just moving VAR on USB box D because it is a network socket, but it's also a file. So you can just move it and you can move it to somewhere else like USB box D.real. And then you can start your own server on VAR on USB box D. And what then is going to happen, right? So Xcode, for example, it will connect to deep proxy first. And this gives deep proxy a chance to decode all the messages into plain text and human readable, I mean, kind of human readable formats. I understand them, but I hope others do as well. And then deep proxy basically decodes and dumps everything to a file. So what you can do with it is, for example, for Xcode, right? So let's say a new iOS version comes out, iOS 15, and running web driver agent doesn't work anymore. So basically what you do is you fire up deep proxy, you open Xcode, you run web driver agent, you stop deep proxy and then you look at the network dump that it created and then you would see what exactly changed. That's the whole idea. So the cool thing about this is that with deep proxy we can implement any feature the Mac has, right? So regardless of what it is, if a macOS tool can do it using the USB cable, then we can figure out how it works and we can implement our own. It allows us to quickly check and debug new iOS updates, which is nice. And then if somebody wants, you know, you can even build cool products on top of it. So because as deep proxy basically is able to decode and re-encode things on the fly, you can optimize protocols for remote use if you want, or you can filter stuff, modify things, right? So if you wanted, you could even filter the iOS device logs with it. And like the end user wouldn't even know, right? So you would basically have a tool that transparently for Xcode and anyone else filters the logs or I don't know, prevent pairing the device if you have a few people using it remotely and you don't want them to pair it, which can be a problem sometimes. And yeah, basically that. And okay, now let's see if this actually works. Let's quickly look at a short demo of deep proxy, right? So okay, cool. You can see that a device is connected to my machine, in this case an iPhone XR. And like I said, right, so you see the nice JSON output, you don't have to do any parsing. It's perfectly valid JSON. And so now let's run oh, I was deep proxy. Okay, and then let's hope it works. Okay, so I wanted to run one. I wanted to start Repdrive agent on the device. And then you can see I made an effort to give actually meaningful error messages, right? So it says, have you mounted the developer image? There's something that Xcode usually does automatically. But since we don't have any Xcode on Linux, and I mean, I'm running this on a Mac, right? It also works perfectly fine on a Mac, by the way. But Go iOS comes with a tool that automatically downloads and installs developer images. So now we have the device ready. Let's try and run Repdrive agent. And here we go. Repdrive agent runs. I could now start an Appium session if I wanted. Right, again, this will work on Linux and it will also work on Windows. But I'm just using my Mac for convenience. And now here's the deep proxy output, right? So the cool thing is deep proxy was running in the background the whole time. And it dumped like some pretty interesting information to the console, right? So you could actually see here which information was being sent back and forth. And here, for example, you can see the WebDriver agent invocation, right? So for example, here you can see the device usually calls XCT log debug message on Xcode, right? So it basically tells Xcode to invoke this function in Objective C, which has a string as a parameter, which is the log message, right? So this is how logging works for XCTesting. And yeah. So you can basically see all the raw output. And now if you stop the proxy, right, like I said before, maybe make this a bit bigger, right? So it's moving back the original socket. And now the device should work just as well as it did before. Nice. And what we can see is that deep proxy created a dump directory. And this one contains all the connections that were created during the time that deep proxy was running. And now we could basically go into one of those and actually check out what happened, right? So we're not going to go into too much detail about this, but you can basically see all the messages that were sent and received by deep proxy. And I just have two more slides and then we can go to the Q&A. So special things goes out to my sponsors. First of all, David Harkowski, who's also giving a talk about his cool project control floor. Thanks a lot, David, for your support and sponsoring. Second sponsor, Sitesp.io. Thanks you all for sponsoring the project. Really happy and grateful for this. And then Nikola Shabanoff. Thank you also for sponsoring the project. Really, really kind of you, and I appreciate it a lot. Now thanks for listening. I hope you found the talk interesting. Now is the time to ask me more stuff. And of course, I will shamelessly do some advertising for my company, Checkly. Let's you combine your entire testing skills with production monitoring, which I think is pretty cool. And if you want to try it, go to check.do and see how fast you can set up an entire test that you could use for production monitoring. That's it. Awesome. Thank you so much, Daniel. So I think we'll try to answer as many questions as we can right now. So we have 14 questions right now. And the question that is asked, is there any similar to any similar to Gstreamer for Mac CLI? Gstreamer? I'm a bit lost. What's the question exactly about? Can I see it somewhere? Do we have it in the chat? Yeah, we do have it in the Q&A section. Okay. So I mean FFMpeg, right? So Gstreamer is what I'm using for a quick time video hack. I like Gstreamer. It's a cool open source video transcoding platform. But yeah, you can also use FFMpeg. So the next question is can you use it also for simulators? Yep. So partially, yes. What's interesting is that for running XUI test and studying WebDriver agent, you can actually use it. So it will work because interestingly, simulators expose a socket that lets you directly talk to the same service that real devices use for running tests. So you can make it work for simulators as well. Actually, that's how I first developed all of this. So I figured out that simulators have that. And so it was a little easier to get raw information and debug the DTX protocol and figure it out. So yeah, the only downside is of course that you cannot actually run your simulators on Linux unless you have some virtualization. All right. So the next one is is this one legal since I'm not sure Apple will be happy on this one? So I think it depends on which country you're in. So for Germany, since I'm not having any like financial gain from this, I think it's okay. Should be covered by German laws regardless of what Apple's weird licenses say. As you can at least here reverse engineer anything just for educational purposes and then make it freely available. About Apple, I think they actually don't care. So Apple usually they don't care about all of these, you know, small projects because you cannot jailbreak a device with it, right? So it doesn't really matter to them and the use case is so small. And Apple is like a mainly consumer oriented company, right? So unless you buy iPhones from them in the millions, they don't care about you actually. So I don't think they will notice. Okay. Thank you. So how can we read iOS device logs? Mm-hmm. So you can run. Maybe I can just show it. Let me share again quickly. And this is the right. Yeah. So all it takes is go iOS log. And now you can get logs. And they're also in JSON format again, right? So I mean, it's like you cannot really stream JSON, right? So I cannot send an array. So you have to do some at least check the line breaks. But still it's perfectly valid JSON. So easy to consume. So yeah, syslog is the command. Yeah. Another one. Just to clarify, is it possible to interact with the device or only see what is happening? It's possible to interact with the device, right? So you can basically do anything that Mac OS based software can do with the device, right? Like Xcode is able to run the test and install an application. You can do this with go iOS. You can pair the device. I recently added a feature that lets you even pair with the device without this annoying trust pop up. So this is especially cool for remote use cases, right? Where nobody is there to tap on the device. You can actually pair the device without any human interaction, which is quite nice. And yeah, so yeah, you interact with the device. So it's can we do a parallel execution with multiple physical devices? Yep. How can we read iOS device logs? Okay, we had that. It's a iOS syslog. How does this work with signing stuff? Yeah. Yeah, signing is an interesting use case. So we thought about reverse engineering code signing. The thing is, you can totally do this. But we figured back then that it's probably not worth the effort because what we ended up doing is just wrap the code sign command with the HTTP server and then use it like one Mac or some... You can even use GitHub actions, right? I actually thought about writing a little demo about how to use GitHub actions to code sign your application. Something like this is what I would do because usually you don't sign these applications a lot, right? And if you sign them, you already have a Mac because you also need to build a package to the application. So what I would say makes more sense is to just use a Mac for signing and not be concerned with that. But yeah, I mean, if you want reverse engineering code signing is totally possible. And there is an old Sasslabs project where they did it, but it's not maintained anymore. So there's some prior work. So can we do continuous integration with this setup? Yep. You can do that, yeah. How does Go iOS keep itself updated with the latest Xcode? Yeah, so right now I do this. So I have some GitHub sponsors. And I just regularly check new devices. For example, the iPhone XR that I was using is on the iOS 15 beta. So it's pretty new. So I just make sure that the latest version works. I usually don't support actively like older versions. So if you want to run it on iOS 9, it might be that it doesn't work anymore. I mean, it's totally doable to add support, but right now it's not a focus that I have. Okay. I think we'll answer one last question. And then we all can move on to the Hangout session. So the last one for this session is can APM server run on Dr. Host A and the emulator and simulated devices run on Mac Host B. Yeah. That should work. Yeah. Okay. I think we can have one more question. I still have time. Can you use Go iOS to get performance metrics of the device? In theory, you can do that. I just haven't implemented it. There was so far nobody was asking for it. So if it's an interesting use case for you, just add a GitHub issue and like this should take around like, I don't know, maybe one or two hours to implement. So it's not a crazy effort. Basically, all I do is fire up deep proxy, record CPU metrics, write down all the method implications, and then reimplement them with Go iOS. All right. Okay. So I think that brings us to the end of the session. And thank you so much guys for joining in. And thank you so much, Daniel, for the great session. I think we can see a lot of like great reviews on the chat of how much they think it's really helpful, your session.