 Let's kick off slide one with some audience participation. So, hands up, logo recognition time. Go and hit me. The youngster at the back. It's Mosaic. Yeah, so let me take you back to the very early days of the graphical web. Here we have, yes, the Mosaic browser running probably on Windows 3.1, I guess, 94, not my screenshot. And the nice thing about this is that you can still get to this page if you use the web archive and pull it up on Chrome, and it's still, the beauty of the web writes, everything still works, more or less. So yeah, this is a pull of the Mosaic browser homepage. Early graphical browser, certainly the leading browser on Windows at the time, and X Windows, although it was, as I remember, big and bloaty on X Windows. There were other browsers too, but the thing I really like about this is circa 93, four, the beginning of the graphical web and I bring it up because I feel like with WebAssembly, we're kind of in a similar, maybe not quite so significant, but probably the most significant event since in terms of technology, that's gonna empower the next wave of web applications. One thing I wanna remind us about is that we don't use the word cyber space often enough anymore, so I really kind of missed that. That was such a big thing. I think we should maybe use WebAssembly here, community to bring resurgence of cyberspace. The extra cute thing on here is that back in 93, 94, the Mosaic team, humans kept track of everything that was new on the internet, and every new website that popped up, they noted it and they created a page and they updated this page with what's new on the internet, which was pretty incredible because of course the internet at that time maybe didn't even change day to day. It might be a few days, nothing would change. Of course, not much like that now, every minute. We've got just an insane amount of content creation and content consumption, but back, let me take you back to the 1st of May, 1994, one of my favorites here is that the American Psychological Society has joined both the internet and the web, and they did this because they established both a gopher directory and a WWW, so even in 94, we were a bit skeptical about whether this hyperlinking, gopher was a king of hyperlinking. It was more popular than HTTP, probably until about this point in time. We hit the inflection point and the image tag probably sealed the deal and we were off to the racism. Arrespo Observatory, some really important scientific stuff going on, and up at the top I got a crappy little quiz that I wrote in my off time when I was studying computer science. So I've been doing this now nearly 30 years, and so I was trying to bring my journey of writing crappy web applications right up to date, and I might even have time to do that live in the demo. So yeah, my name is Liam Crilly. I work at EngineX and this talk is about server-side web assembly and mostly about web applications, frankly. So let's get into that. Jumping back to 94 again, and this is my screenshot. I'm so glad I took it, and I still have a table with a date stamp, so I know exactly when I took it. I had my first web app, like everyone else. It was like, come sign my guestbook, and later on I added a counter and we knew how many people have visited three or four or five. But this is HTTP 0.9, straight out of CERN, HTML1, straight out of Tim Berners-Lee, and you could build web applications. But if you know your early HTTP standards, we just have a get. There is no post, there is no file upload. We just got images, we just got fill out forms, but all of these things were ad hoc extensions on these very, very nascent standards, and everyone kind of played catch-up. So Netscape did a thing, Mosaic did a thing. We like that, we don't like that, Blink Tags, et cetera, and the browse of what's happened, and like innovation ran forwards with standards caught up. So how do we build web applications in back in the day? Well, we had no post method and we had no bodies. Well, you could do things like this. This is version two, by the way, of the quiz, and you could have fill out forms and radio buttons and do questionnaires. You could put search indexes on file systems and look for stuff. You could do interesting, useful things. I could write some software and have my mind blown that that software could run on someone else's computer and they didn't have to install any software. Amazing. And we did it, we hacked the hell out of the query string. So we had query parameters and we just ran them all the way until probably the one K limit. And you could build a surprising amount of stuff this way and stuff got built. But all this stuff just pushed forwards without standardization and there are a lot of mistakes along the way. So things evolved from here and as much as I'm interested in front end development and progression of web apps, I'm gonna focus on the back end side here. If anyone wants to reminisce about Flash, we can do that after the talk. So these first web apps, these things that we're hacking on the end of the query string and passing parameters across and doing useful jobs, we lent on the underlying Unix and POSIX systems. So we sent stuff across. When we got HTTP one, we sent stuff across in standard input. We got stuff back on standard output. We use file descriptors in our applications or whatever, or shell scripts. Whatever we were running. And we sent the HTTP context by which I mean URI input headers when they arrived bodies into the application. That's our context. Then we have a context that's got sent back to the client. The status code, 200 okay, 404, whatever. Again, a bunch of headers and then the response body, that's the thing. And so this HTTP context was made, try to bring this into the application. So developers, people that was hacking around trying to sort of figure out what we could do with this new technology were able to leverage this because the web server would just fork a process. These environment variables and file descriptors were available to the application and we could build stuff. And that took a certain way, but it didn't scale. And we had things like VAR-CGI come along and we were able to manage processes a bit more smartly. And then 96, 97, we got all the components that the LAMP stack came around. So PHP, MySQL thing came around that time. The LAMP stack just was an amazing marketing wrapper around these technologies, but it gave people huge confidence that you could choose these tools, get support, use open source, and this powered really the next wave of web applications. It powered the first generation of Facebook. So LAMP stack took us way forwards and because PHP had this HTTP context available to the developer as a principle object, like a primitive in-cycle language, with your types and data structures that you could do. I wanna get ahead of, easy. So that was tremendous and it had its own challenges. And then the next wave comes around when web native languages come along. So things like Python, Ruby, or Ruby on Rails in particular, made the next kind of leap forward. And every time we do this, we give developers more choice and we provide this HTTP context in a more and more native and natural way to developers. Backend frameworks, like yes, I mentioned Ruby on Rails, one of my personal favorites, Flask for Python. And then the coexistence of the back end framework and the front end framework to provide a really rich front end user experience that wasn't completely re-implementing what's happening in the back end. That sort of brings us up to date. The latest cloud native languages such as Golang and Node.js kind of take this little one step further because what they do is they bring a web server with them. So now a developer doesn't even have to throw up a dead web server in front of that application code. Node.js and Golang have these things, they're just an inherent part of that stack. And that's good and bad. So when you deploy those applications, Golang or Node.js in production, for example, you put a reverse proxy in front of them because you kind of need to guarantee a bit of scale. You usually need to do a few things at the HTTP layer that the application layer shouldn't or doesn't want to do. TLS, who wants to build certificates into the application code? People talk about doing end-to-end TLS. You go to the reverse proxy and the application does the rest of it. So what I'm advocating for is decoupling the network layer that handles HTTP and the runtime. So that's courses for courses. What happened in these earlier stages is that we allowed the web technology, the network technology to go at its own pace, innovative at its own pace and the application runtime to do the same. And they were both able to go on their own journeys and be extended and innovative in their own direction. And everything kept working. Like you had, you followed your Apache lifecycle and got more and more features. End-to-Dex comes along, does the same thing. Tomcat, IES. The application code, and the languages, are able to do their own thing as well. And as soon as we bring the two things together, we stall that innovation. Not to mention separation of concerns and security boundaries and giving people choices and independent control over changing each part of the stack. So, come back to this point. Because what do developers really need? They want to be able to write their business logic, write the application code. They tend to care absolutely nothing about the bottom of the stack. Certainly not the network layer. And they don't want to be introduced to it either. They might be happy understanding what an HTTP request looks like in HTTP One and what a response looks like. And that's the input and output into the system. So, here we have a very simple, let's call it a JSON REST microservice. If you'll indulge me. That takes an array of numbers and adds them together. And someone has to deal with the floating point rounding error. And so this is what the application code might look like. This is what a good developer experience looks like in this case. And I'll use Ruby again as an example. Getting access to the HTTP request body is a very simple statement. Sending back an HTTP status code. It's sort of an implicit part of the response. It's not even, there's not even a declaration about what this is. It's just, okay, this is the end. Though it must be the status code. This must be HTTP. And it allows and allows developers to build really interesting applications. And only have to deal with a little messy bit in the middle where the business logic is. This is an absolute slam dunk for WebAssembly, right? We've got this request response patterned HTTP. We've got nearly, like we've got 30-ish years of HTTP infrastructure, tooling, experience, middle boxes, proxies, caching engines, cloud services. All this stuff is ready to deliver applications at scale. We've got proven at hyperscale with the world we live in today. And we've got WebAssembly, which once bites in and bites out, okay? HTTP request, HTTP response. This is just too tempting. We've got to build some really interesting things here. And so the group that I work in, the NGINX saw this and thought, well, you just have to do this. This is gonna not only be an amazing plugin model for extending like the NGINX proxy that we already have, but it's gonna be able to change what things look like at the runtime. We just need network layer, TLS layer, HTTP parsing, request routing, and code execution. Code execution is the only thing we really care about here. Everything else is a solved problem. So let's separate these concerns. Let's minimize the application code to the sandbox, the WebAssembly runtime, because we've got pretty good solutions for handling lower down the stack. And we know the developers don't care about lower down the stack. So this kind of became a plan. So let me, what we have to do right is just do some magic in the middle and get this HTTP context in and out. So as I mentioned, right, I've worked at NGINX for a number of years. I take it there's some people who know NGINX in the room. Anyone using NGINX today in their more or less daily lives or have used it in the past? Yeah, awesome. Anyone used NGINX unit or heard of it before? We do a great job of keeping this a secret. This is a six year old project that was created by the original creator of NGINX. And after that, he wrote a JavaScript engine for NGINX and after that, he wrote units. And because it's from the original creator of NGINX, like all the things that he wish he'd done differently in the first version had been sitting on the shelf as prototype code and we kind of, a few things came together at the birth of this project. So yeah, it's an HTTP engine. It's a web server, it can serve static files, but the main difference between the NGINX project most people already know is that it is really supposed to be an application runtime. It's veriting application code. And so we've gone through this period from the lamp stack onwards where we tend to have a web server reverse proxy, something that serves a static files, something that does the dynamic content. And Node.js is a great example. The reason you put a reverse proxy in front of Node.js, not because Node.js is bad or is slow, it's really great, but you probably wanna serve your static files in a simpler way. And so reverse proxy sits there, routes to Node.js, serve static files. The sort of mission of unit was to simplify that stack. So we wanna be able to basically more tightly configure the web server to know about the application code rather than hand it off separately. But we didn't wanna tightly couple it, so it's a multi-process system. So there's a configuration control plane not shown here, but most of this work is taken care of by the unit-demon router. So it's got Nebuchlayer, it's got TLS layer, it's got HTTP parsing, it's actually a little bit faster than EngineX HTTP parsing, and it's got a really nice request routing layer. And then it's got a shared memory model where it manages the processes of the application code, in this case, Python. So we talk in the language of the HTTP context for the programming language. So in Python's case, we have WSGI, we have ASGI for the async model, depending on what you're building. PHP has SAPI, there's a number of different languages, each with their own interface description for how the URI and the headers get across and how they get back again. And so it's felt like just, this is gonna be easy. This is not our first rodeo, we've done this at PHP, Python, Ruby, Perl, Go, no, Java. We've got all these backend web frameworks that we know work really well. And all we're gonna do is we're gonna drop, we're gonna basically do some copy paste code and we're gonna drop WebAssembly in and it's gonna be great. So this is the design, we've already got this system, it's got HTTP context, we've got this nice fast shared memory system which passes across to the application runtime. All we need to do from the HTTP context, which is now in another process, is initialize wasm runtime. So yeah, let's use wasm time, let's have it load the module and we'll have our app process, basically tell wasm time what to do, initialize the memory and we'll just copy the context in, business logic can run and we'll send it all back again. Now we chose wasm time, A, because it's nice and mature, B, because it was written in Rust and we didn't have a Rust experience for Unity. So we always like to add more languages and the more we can cover, the easier it is for people to have a good experience with multiple languages. And by the way, you can run these multiple languages on the same host or in the same container because it's one process managed at the top and it manages it on the other piece. And because it has nice C bindings. So unit is written in C, the main engine projects written in C, we've got lots of really good C developers and that's the go-to choice. So we set off about three or four months ago to execute on this plan and no one else really knew anything about web assembly and we kind of scraped our way through and found out where things are happening and how to make it all work. And it was tough. And so I'm speaking for the team. I'm not a C developer. I'm on the, I've seen a lot, right? But I'm on the product management side of the house and I just watched the team through this process. So we started where you would start in the simplest possible way. We built a kind of CGI. So this is the initial sort of forking model and using POSIX standard to sort of, WASM time has lots of WASI stuff built in so we could do the standard in, standard out trick. And we knew it was gonna be throw away because we know it doesn't scale. So our application process launched WASM time. We call WASM time memory grown, allocates some space for the HTTP context to come in and that seemed to work more or less. And then we started writing our first WASM modules to test in this environment. And because we've got C developers, they wanted to write it in C and then compile the C to WASM. So, okay, it's not where I'd start, but yeah, go with your tool chain of choice. And that also kind of worked until anything beyond the most simple, small request would just take full. So things fell over. We had memory allocation happening in our app process calling WASM time through the WASM time memory grow. And then we had the WASM module allocating memory inside its own linear memory space and that they were just bang into each other and collide and we had lots of, lots of problems. So, turns out all of this was mostly avoidable because not many people are gonna be writing WASM modules in C. It's not, the benefit's not really that great. And the, turns out the C tool chain is one of the least mature WASM tool chains, but we didn't know this yet. So we banged our head against the wall. We had to look at the, this is the, at the top of the slide here is the default WASM C linker stack and heaps layout, memory layout for the WebAssembly runtime. And the data was very small and sorry, the stack was very small. And so we took these three steps to finally get some reliability in our ability to write C modules that compile to WASM. So we just memory layout to the bottom set. So we separated the stack and the heap and we made the stack size a lot larger. And then we stopped trying to allocate memory from afar. So we just let WASM time initialize and then within the WASM module itself, it asks the memory that it needs. And what we're doing today is that we're basically allocating 32 megabytes and all of the HTTP context in and out goes across this 32 meg, unless you need more. And we do this in a streaming fashion so you can have like multi gigabyte file uploads to a WASM module and you can do it all through this 32 megabyte interface. Unless you want, you can write that out of the desk for example without needing any more memory. So we know this is a model we use elsewhere, sort of buffering trick in NJX to avoid large memory growth. And this seems to be a model we can also apply. But to get this working reliably, this turns out to be the set of linker flags that we need to compile C code to a WASM module for our runtime interface. And so as you can imagine, this isn't the first iteration of that flag. We now have a library that hides all this for you. So like everybody else, we've had to write an SDK to make the developer experience close to approachable. Okay, so going back to really what developers need, we want a really easy approachable way of just focusing on the business logic and having access to the HTTP context in and out in a really easy way. And this is our, nearly fits on a slide, right? And this is our best effort today. So we have a Rust SDK as well. And I know that everyone that's tried to do a self-signed web assembly has had to build an SDK because the standards aren't there yet. But unlike 94, 95, 96, like the whole community is working together to get that stuff right, and we're not gonna have this sort of bifurcated sort of a Wild West standards force. So it's not super pretty, but we don't have any unsafe blocks in our Rust code. We can now write Rust and this is, of course, this is the simplest sort of hollow world. We have access to bodies not shown here. So I'll ask you to squint at this and imagine it's an AI inference model. This works. And I think I'm gonna push my luck and see if I've got enough time to do a little demo here too. So let's try this. Here's some Rust code. So I'll take you back to the HTTP example that we saw earlier of adding numbers together. And let's take a look at this. This is quite old code, so I do still have some unsafe. So let me get through the initialization phase and scroll down here, right? So the allocate 4K for all the headers, which is usually four AKs, usually the limit web servers anyway. We've got a dependency on this JSON provider. Here we get the context of the request, request body, and then we do some actual Rust and iterate over this array and the numbers together. So again, imagine this is an AI model instead. When we just hack some raw JSON back and send content type header. So there's a complete web app, you know, six-yard lines of Rust, and let's run it. So I've got unit installed. As you can see, I got this from HomeBrew. Sorry, yeah, I did, yeah. So room install unit, unit wasm, gets you everything you need here. So every runtime is a plug-in module, so it's not one big, bloaty runtime. So I'm gonna start it up. I'll attach it to the console so we can see what it's doing. And we'll see all the, if I have a problem, we'll see the logs on the output. Oh, yeah, I meant attach it to the console. Okay, so it starts, multi-processes start, look at all my available runtimes I can use, so we're just gonna use whereas I'm here. Is 0.1, is a tech preview. And right, I've got a controller for config, I've got the main process that handles all the processes and I've got the router process. So that's good, let's build this Rust. So it's a library, we'll call it add. Let's copy that Rust file into lib.rs. And now, let's add the dependencies. So, unit wasm is a dependency, we have a crate for that which is influence the SDK, so we don't have to worry about any of that. And I needed that JSON library. And I think I'm ready to build. And I can't remember the wazzy32, maybe build will help me. Yes, so half of these are just to get JSON. Done, so let's have a look at target. Wazzy, I didn't do anything smart, so I got. I forgot a thing. So, this is a library, not a console application. So, we decided that we need a dynamic library because it's gonna be loaded by Wazzy32. And so that is, all right, I just gotta cheat add this to cargo.toml. So, I need to say that this crate type is a dynamic library type. And now I can build it again. And now I should find a .wazm file where I expect to find a target wazzy debug. There it is, all right, so add.wazm. Just because that's a null, I can't type all that. I'm gonna copy it down here. And just to prove it, all right, tell you what, let me just get rid of that. I have one .wazm file, and that's what we're gonna use. So, unit started, I'm gonna use, it's configured with a JSON REST interface. So, there's no config files apart from state. So, I'm gonna use the unit C command line tool to see what, it's listening on a unit socket, which is why it's a bit of a painful to use curl. So, here's the current configuration. We've got listeners, routes, and applications. Let's edit this. So, I'm gonna listen on port 9000. I can do all sorts of things. And what am I gonna do when I get there? Pass it to the routes. So, I only got three objects to manage, listeners, routes, and applications. As you see, routes is an array, so we get things in order. And I'm just gonna put like a default action here just so I can make sure it's working properly. Love typing JSON. That should work. Every request to this web server is just a mutual for, but anyone that can pass JSON in real time is welcome to help me. Tell you what, I'll do one thing at a time. Yay. Okay, that's now their action. I like that. Let's check it. So, let's get the wazm running, and I'm not gonna type this because it's a little bit gnarly. I've got a app.json. So, I gotta define an application called add. Type is wazm, so that knows what module we're gonna use. And then the module itself, I sort of have to, we have language modules and wazm modules, and I can't do anything about the ambiguity, so, sorry. So we specified the module itself, right? So I ran this from the current directory that I'm in, so I'm just gonna skip the whole path thing. And then these are all of our SDK helpers that allows for memory allocation to happen safely inside the wazm module, and how to initialize and end the handler itself. So I'm just gonna pass that straight to the application's object, so I can reference any of these configurations directly. And that's done, and in the log output standard error, we can see that we started a prototype, which makes spawning new processes fast, and the application itself. And I've hit PS, I've now got, so unit is now managing the application processes. There's a bunch of other config, I can say scale to 20, scale down to five, and it will auto scale the rest of it. One last thing to do, I need to attach a route to this application. So config routes currently is just doing 204 for everything, so let's do a new add one more object, and this time we're gonna have to match on something, so match on anything. Let's match on a URI of add, yes. And what happens when that matches is the action. Pass this request to the application's object, that's add, we just created it. So that should stitch that together. I can match on headers or methods, I can say it has to be a post, right? But that should be enough. Still can't do JSON, let's try it again. Match on URI of add, and maybe that was it. Let's see the whole config. All right, listening on 9000, all requests go to routes, routes will match on slash add or fall through to the default. So I can still, let's get my, I still send some garbage URI, I'll still get 204, but let's now construct something a bit more useful. So I'll call slash add and nice thing about this HTTP tool is that I can construct JSON without typing it. As we know, that's challenging. So let's add some numbers together. So we sent this object and some wasm ran and gave us an answer. And there's like, there's no other magic here. There's nothing that's happening. There's only the add to wasm file and there's only the processes that can just manage. So the developer experience for this allows for Rust developers fairly easy access to the environment that they know well, tool chain they know well, and we literally just need to drop the one file in. And this really is the why we're excited about wasm in general and server-side wasm. So I've now got that wasm file and I wanna be able to push that anywhere. I wanna run that in your cloud, my cloud, someone else's server. I'm gonna take that and self-host, run it through a test framework, and I've got one build pipeline. And that's, as well as all the other amazing advantages of WebAssembly around performance and security, this build once, run anywhere, promise is, like that's the real deal. So as I mentioned, developer experience is king and this only works if the developer experience is great. It only works if we have universal portability across all of the server-side runtimes. So we can't wait to throw away that SDK we just spent months building because Wazzy HTTP and the Wazzy cloud world are the things that we really need for this stuff to take off. So this is the beginning of our journey. We're really excited to see what's already planned for Wazzy HTTP and Wazzy Preview 2. And we start work on that next week. And if that's true, we're in a position to have the same sort of burst of innovation and separation of concerns that allows these Wazm files to go anywhere for the utility of Wazm and the future of server-side web applications to take to the next, yeah, web apps to the next level. So that's it. I hope that's interesting. I'll leave you some notes about the NGNX unit project. If you're wondering, well, what about NGNX proper? That's why I came. This is the learning we'll be taking into that project to allow for extending NGNX with Wazm as a plug-in model. Feel free to, I think I've gone over time. So you can grab me after, grab me at the booth or drop me a note. Thanks.