Hello and welcome to this month's edition of Small is Beautiful. My name is Aral Balkan and it's going to be just me today because Laura is busy. And today I'm going to take you through something that I've added very recently to Kitten: the ability to create end-to-end encrypted Small Web applications and peer- to-peer Small Web applications. I mean, that is actually part of the nature of what the Small Web is going to be but so it's quite exciting. I've gone beyond - a little beyond - the kind of infrastructure work I've been doing in the last few years... building upon that now, we're getting to the fun bits. So yeah, let's see... I think people are already joining. Um, yes, hello. Jovial Hopper - you can change those names if you want to - Hi Olwe? ...I'm not entirely sure how to pronounce your name. Hello! So we have some folks joining which is kind of cool. All right, so let's get started. so I'll start from the very beginning. I'm not going to assume, you know, anything about kitten or how to get started. So I'm just gonna pull up a web browser here. And actually let me just give me one second. Sorry. Just going to set my system to light mode because it's just gone 5pm and it's gone to dark mode. I want to demode and light mode for you. So oops, that is the chat, let's go. It's the web browser. Right, so I'm just gonna pull up a web browser here and take you to the kits and website which is it's repository right now, which is all code work. So it's codebug.org forward slash kitten forward slash. If you go over there, you'll see that kitten is a web development kit that small person loves you all true. And there is this warning that you should really heat. It's not future complete. It's not ready for production. Use if you are a developer, you can play with it just don't really deploy stuff with it and what I'm going to show you today with the basic end-to-end encrypted chat example, for example, don't take that and deploy that or use it for, you know, actual communicate private communication. This is just to show you kind of the basics of how to do things. And also I love the fact that I can actually use these examples to quickly prototype and new features that are going to come into kid and move some of the feet functionality back into it. Anyway all that to say, you know, very early days right now. So, let's see what we're going to build. so Kitten, so for first of all, if you want to follow along if you want to install it, let's go with the installation. Um, you can either clone this repository and run dots forward slash install. You will need node.js for that. You can do that. You can do that right now if you want to follow along if you don't have no JS installed or if you want to if you trust that the installation script that you get from code Burke is the one that I have here, which you can take a look at and if you trust that you trust piping into your shell then you can install it via wget and curl as well. So let's just do that here. I've already got installed, but I can install it again and it will just install the latest version from From the repository over what I have installed locally, so it'll be just like what you're doing. So, let's bring up my terminal here. And I'm just going to paste that so I'll just use wget. You could use Curl as well. So it says installing kitten web install delay detected blah blah blah. Let's take a look at what happened installing. Okay, it will if it finds if you're on Linux, there's this gorgeous security theater. That's like A remnant of the Mainframe era things think sometimes change slowly in the Linux world called privileged ports, which is great when you were on mainframe computers and that was a trust model. It's actually almost an anti-security feature these days. No other major operating system. Has it Mac OS removed it for example a few years ago, but we're stuck with it for now, but it means that we can't bind to Ports under a certain number so we can't bind to 80 and 443 which the two that we're interested in in web development for testing locally. So Keith, you know trying to keep your local test environment as close to what you're going to deploy with. It's always a great idea and that's something that kitten really adheres to that's why you test over https locally as well. That's all. Yeah just done for you don't have to worry about it. Anyway, it will actually set that on your computer. It will lower the privilege report so you can actually buy into 80 and 443. And again that does not put you at risk because you are not automate frame. All right. I hope I mean, I don't know. If you are then don't do this do not do not run this on a Mainframe computer from the 1960s big security hole for those. All right. So anyway, and we can start using kitten. So what we're gonna build today is a peer-to-peer end-to-end encrypted chat using kids. so if we go back to the kitten website and if you look down there. You'll find that there is a getting started. A guy does not even a guy. It's actually tutorials. So it takes you through, you know, very very basics of things. Like how do we just create an HTML file? And then run that and then how do we you know count kittens? I'm not gonna go through all this today. You can you can look through it and you should as well if you're interested in kitten you should look through it and really kind of see you know, what? How it works and it's assumptions Etc because it's quite different to what you might be used to but where I'm going to jump to is so there are lots of these examples that you can see here. There's stuffed with the fatty verse you can get posts from my favorite from my Macedon instance, etc. Etc, etc. And then we get to kitten chat. So there's a whole tutorial on it. You can also find all of these examples if you clone the repository, they're only the examples folder so you can just run any of these examples. So, you know, hey, let me show you so let me show you just the very basic version of what we're gonna start with and what we're going to build on which is the persistent kitten chat example, so to do that, I'm going to bring back my terminal here and I've already got Kitt the source code of course surprise surprise on my computer. So if you look over here, this is the kit in the source code. Just what you saw in that repository and we have the examples there. Okay, since we're going to build upon an existing example, I'm actually going to make a demo folder. and I'll go ahead and copy the persisted kitten chat example, I'll do that recursively so it gets the the folder and I'll put that into my demo folder so that we can just work with a Fresh copy of that. So now I'm in my demo folder. There's the persisted kitten chat example that I got and this is all it is really let me look at how few like how few source files there are I mean look at the sizes as well. You can see that you know that this is Tiny. It's it's tiny tiny tiny. I'll show you the source code as well going forward, but just tiny and notice what you don't see like like where's the bill process or the build configuration or where's the where's all the stuff that's generated for you when you start a new web project. We don't need any of this. We're not doing big Tech here. We're not doing big web. We're not working in an Enterprise, you know, so we don't need any of that. There is no build process with kid. All right. So, how do we run this? Well, it's it's pretty simple. We say kitten. Um, and that's it. And the first time it runs as you notice there was a like a little delay there. That's because kitten itself has some has a web application and some roots that it kind of intermixes into your applications. We'll see what all that's about later. It's for authentication Etc. So you don't have to build them. But just like any kid in application if there are packages that it uses kitten automatically runs npm install on those and again, you don't need to have node or npm installed kitten treats. No JS as it's runtime and it installs the version that it needs that it wants and and it's installs mpm as well the version that wants so you don't have to worry about that. It doesn't mess with your system or anything. It just installs a binaries for your earrings Let's see. We have some chat comments coming in. Let's see what the comments are. Let's see. You don't want to let any random user bind to commonwealthifying ports on a server system. If you're the only user on the system. It's no problem. Exactly and I'm the only user. system Harold yeah, exactly and the thing is today these days we have personal computers. So usually it's one person on there. So yeah, let's see. People are sharing it. That's nice. Toasterson says hello. Hi. Oh, I pronounced it. Well, okay. That was always that was entirely an accident. I am glad that I did though. Okay, perfect. Let's get rid of those. Right. So here we are kittens running. And so let me bring up my web browser. Oh. Push it over here and let's just go to https localhost. And we see here the persistent kitten chat example, and it's persisted. I was already testing it clearly. And didn't delete the database. All right, that's fine. At least you know, it's not a lie. It is persistent. Let's let's also open another window. here Let's see. Let's There we go, and let me go to localhost there as well. Now. This is a very traditional websocket chat, right? What am I mean by like traditional web based web socket chat. So here I'll make this a roll and I'll make this also all Notice it's centralized. So we both go to local. So it's just being served at locals just like the web normally is and here I'm just gonna say hey I'm back. And from here, I'll say that's amazing. Wow. All right, whatever. So there you go. You can see it working. There's the that's a whip socket check very traditional very much like, you know, you go to a single domain and lots of people go there they congregate at centralized. You have a chat. All right. Nothing. We haven't really seen before what we probably haven't seen before. Let me actually close. This is Is how it's built in kitten? So let's start by taking a look at this first of all. so again, these are the files that are in there. So let's take a quick look at what those files are. So I'm just going to open up my editor. I use Helix editor. He looks Dash editor.com if you want to look at it, it's pretty cool. I really enjoy it. Let's start with the chat dot socket. Okay, so actually you know what? You know what no here. Let's start even simpler. Okay. Let me show you how a socket Works in kit. So I'm going to make a web socket demo folder. Okay, there's nothing in here. I've started a new project. Okay, and I'm just going to create a file called chat dot sockets. All right. So let's just start from scratch just so you see there's no magic here. All right, so let me start my Helix editor open up that chat dot socket. So what's a socket root in in kitten? It's just like any other kitten Roots so you export a default. function and it gets a socket. As its first arguments and request the request the HTTP request that originated it we're not going to use that. So I've just put an underscore to indicate that we're not going to use that but what we can do is we can listen for the message events on there. So this basically behind the scenes. This is the Ws module in node.js. So if you if you want to look up how it works I've documented it, of course in kitten as well, but you can mirror the client side websockets API so we can just say add event listener just like we would in the browser and we want to listen for the message event. Okay, and when we get the message events, We want to do something with it. What do we want to do? Well, just for testing let's say we want to send that message to everyone including the person who sent it or the node that sent it to us the client. So all we would do is is we would say well we need to get the message out of there because it's on event.data. And if here for example You're Expecting Json or whatever then you would parse it. I will just test it with strings and we won't even build an interface because we're going to have an interface later. So let's not waste time with that. So just going to test it with strings. So I'll just get it directly and then I will I'm just gonna use that socket that I have and I'll say socket all. And I'll send that message. Okay, so let's test this. So, this is our app. and I mean, this is our app like again there is nothing else to do in kitten you just kind of Write the logic you want and that's it. There. Isn't this whole kind of song and dance you have to do before it works. So I'll just say kitten. And here you can see that it is running over here. How should we test it? I actually have a little tool that I can use in my browser for testing this so let's bring back the browser. Um, and I'm just going to go and select rested which is just this. Plug-in for a plug-in extension for Firefox where you can just make a requests again. I'm not going to use that actually because I'm not thinking I'm actually going to use the weasel websocket weasel extension. Where's my mind? Who knows? Um, so let's connect to WSS, right? Because it's secure Local Host forward slash chat dot socket. All right, let's open that connection. And it says it's connecting and the connection is ready. And so let's send a message. Let's say hello and send it. Now. Look what happened we sent. Hello we got hello. It's just a basic Echo server, right? That's that's all it is if we open another instance of the weasel websocket weasel and connect and here we say hello again. I'm sorry send it then notice the second instance got hello, and we got hello again as well because we're sending it to everyone what if we didn't want to send it to everyone common thing that you might want to do is to send it to everyone but the person that sent the message right so that they don't get it especially if you're doing that. Basically you'll send it to everyone if you're not doing optimistic updates on the client on the interface what I mean, my optimistic updates somebody sends a message and immediately it appears in their chat window that they've sent it even before like, you know that it's been sent and that's usually a good thing to do because that keeps your interface smooth and or if you're not doing that then you would send it to everyone and then the person would get it as well. Once it's gone through that process of actually being processed by the server. So let's just take a look at that very quickly. So I will open up another tab here and go to that little websocket demo thing. Open up my editor. And here instead of socket all let's just say socket broadcast. And if you say broadcast, then it will be sent only to the people who are Everyone, but the person who sent it. also notice the connections closed here and if I bring back my terminal And look at the other tab. Here just scroll up. You can see that kitten restarted. I can restart it because we made a change to the code, right? So it listens for changes on the back end for the front end on the front end. It'll actually refresh your page as you're working on it in the future. We might get more, you know detailed about that how which parts we refresh? Maybe we can maintain State Etc. So if we say broadcast though, I'm just go back. And here let's open our connection again, and let's open up our other. Where's our other? Oh, they're on top of each other. That's not good. Is it there we go. Um, so that connection is open. Let's open up the others connection as well. And now if we send hello then we've sent hello and they've received. Hello because it's broadcast. We haven't actually received hello as well. So this is just the basics of how how let's move that out of the way how a soccer connection Works in kit. Okay. So let me show you what the persisted kitten chat example looks like that we're gonna start use as a starting point to implement the end-to-end encrypted one. So I'm going to go back into my terminal. I'm first of all, let me just see if there are any questions or anything. So let's just bring up the chat here. Hey Rue, Rue says Hi also are all Harold we have servers too. That's that's good. right Yep, some people have joined great. No questions great also towards the end of this. I'll give out a studio URL if you want to come in like and ask a question on camera you can if you want to that's something you want to do, right? So let's go back into our persistent kitten chats. Okay, so Again, this is what it looks like. So I'm just gonna run it first so you can see what it looks like when it's run. So it's just it's really a slightly more polished version of what we just did with the chat socket and using our you know websocket weasel. It's just got it into face on it and it persists some messages. I'll show you what that's about as well. So I'm just gonna say kitten and you can see it's running. So again, I will bring back my web browser. Come back web browser and I'll go to localhost. Oh, yeah, I showed you this. Yeah, we should we looked at this. Okay. So this is what it looks like. And so now this is what happens when you discover a major bug in kitten an hour before and you're rushing to fix it. Anyway, my mind is like All right. So let me instead show you what the code looks like. So we saw the other sockets, right? So let's just look at this the more involved chat socket that we have here to begin with. So here you can see we've got the same we're exporting a function just like we did there's a socket there's a request right? And then we've got over here we've got we're adding an event listener just like we did for a message when a message is coming in though this time. We are parsing it as as as Json over here. We're doing some very basic validation. This is just the basic example just to see if the messages in the shape that we think it is. We're removing the headers that are in the message. This is something that is sent automatically by htmx, which we'll see in a second which is a progressive enhancement on the client side that we're using for data exchange without losing State and we're doing something here which Is unique to kitten. We are adding the message we've received to the messages. Table on our database our Global database. So every kidnap has Global access to this object called DB which is the the database it is a in-process JavaScript database that actually writes to an append only JavaScript log, not a Json JavaScript and it's in process and in memory, so that's what's happening there. If you look at the top you'll see here that we're actually creating that table table in this case array, right and we can just use standard JavaScript to interact with it. It's a proxy and it just persists the operations that we carry out on it. If you want to learn more about that. You can go to codebook.org. Forward slash small Tech forward slash jsdb, and that is the repository for the JavaScript database and you can see what it's for what it's not for. It's not for surveillance capitalists. For example, it's for small data and you can see how you can use it. So in kits and you don't have to do this bit where you're opening the database that DB is what's made available for you, but here you can see that working with it is just like working with it's just basically working with JavaScript objects at db.people equals this whatever. And if you want to see what that looks like, actually when we ran persisted kitten chat, you saw over here that there's a databases folder that we can have a look at. So let's take a look at that databases folder. for every project and domain and Port combination that you run kitten automatically creates a settings folder for that with its databases. So what we were just looking at was Hard to see on this screen over here. Not sandbox. What are we looking for? What do we have a demo? There we go, demo persisted kitten chat, right. So if we look at that, you can see that the the tables are just JavaScript. So that's kind of cool, isn't it? They're just JavaScript. That's what I mean by a JavaScript append Only log. Um and here are our messages. So this has been compacted when it restarts it compacts, but you can see the messages are just nickname and text right and here's nickname and the text that was sent so actually so for example if we wanted to let's see databases. And what is this deployments? No, not that one for local. Okay. Our home are all demo dot persistent kitten chat localhost. Yeah, we ran that there so if we wanted to for example tail that let me just go into my other tab. First of all. and Go to persisted kitten chat and run it. So I'm just going to say a kitten. So we're running that there going back into my other. Tab and I'm just gonna say tail that those messages right? Let's just keep that there and then let's bring back one of the browsers. Over here and go to localhost. And say I don't know maybe this is Laura. She's gonna say I'm on here today. I'm busy. And Laura sends a message notice. What happens this is the append Only log, right? So it just gets added to that array. What do we have to do for that in the code? Just what I showed you earlier almost nothing or we had to do for that if I move this here, let's just Where else there? Okay. So if I Show you the code again. All we had to do. Find the messages. Let's find the next one. There we go was that we just pushed the message into an array in JavaScript and that's what created this in the append Only log. So that's how jsdb Works. Let's see if there any questions or anything about that. Who sent me a smiley? I'll take it. All right, so no questions. That's great. All right, so that's how the append Only log Works in jsdb. Kind of exit that process. All right. So let's just keep looking at how we built this. Persistent chat first of all, so like I said just like in our very basic example that we're getting the message. We're saving it to the database. And then we're doing a sock at all. But here's an interesting thing. We're sending HTML. So, you know you might think why aren't you sending Json or something? Well, this is HTM ax and kitten has a native support for htmx. What do I mean my native support if I go to end of this page? over here Oh, no. Sorry. I'll show it. I'll show it to you on the page, but Basically kitten serves a version of htmex and you can use it from there. If you want to you don't have to use htmx. So what's htmx I can hear some of you ask. htmx good htmx.org. Htmx is basically a hypertext style system. So for example Here's a quick use their quick start. They're just loading it from unpackaged.com. But here's what you need to look at. So you can declaratively add. Behavior to HTML so you can progressively enhance it in a sense with this Behavior with these attributes. So HX post forward slash clicked means that when this button is clicked. It's going to make a post request to the clicked root here and whatever it gets back from that it's going to swap with the outer HTML of this element. So that's pretty cool. And there is a websocket extension which I'll show you in a second that we're using and the persistent kitten chat example, and that's how we're doing it basically. So let's let's go back. over here, so what we're the message were broadcasting to all of the clients that are connected is this HTML this div and it's saying to htmx swap this out of band out of band means not just on the element that this exists on but on a different element. Well, which one well before the end of the list of messages specified by ID here and what we're sending is a message component. Okay. Well, we haven't seen that yet, right which has a message attribute, which is the message. Okay, so A number of things are happening here that are kittens specific notice this HTML template string there. So this is built into kitten. This is how you do templating in kitten using just plain JavaScript and the HTML tag template function can is aware of components can do components. um, so let's just take a look at what that message component looks like that. Let's go to where that's reference. We're importing message from message dot component. Right? And if you saw this remember, those are those are our files message dot component is one of them. So let's go to where that's created. Oh, sorry, it can't because this is nowhere component is so let me just open the message component. And this is the message component. It's actually pretty simple. It's just again. a bit of HTML A list item with the message nickname in a strong tag and then the message dot text. That's it. That is what we are returning. So that's the message component and that's all there is to that. So you can see here that we're just sending that message to all the response to all the recipients and then we're just logging the message that we got and how many recipients we sent it two. That's it. The basic validation we did was just to validate the you know, the two things are strings. It's not even like it's the can't even call validation, but that's it. So that's our chat socket in in the in this in this example. So what about the interface the fun bit? Well, you can see that well for one thing in kitten, you've probably noticed we use extensions a lot to me to to separate different types of files. So dot socket is a websocket. That's how kit knows to send it the socket as the first argument here. We have a page a page the client side page web page and all of these are just JavaScript though. Okay. So the way kitten works is it uses ecmascript module loaders to just load all this JavaScript one of the cool things with it just being JavaScript is we don't need a huge amount of tooling notice that I have syntax highlighting here notice that I have. Oh, I don't know. Let me go to this markup and go to wherever it's referenced. So I have language intelligence right I can go to where that's where that's reference in my editor. So I have all these things because in order to get the full tooling All I have to do is say hey Helix editor a DOT page file. That's a Javascript file dot socket is a Javascript file and similarly with my ecmascript loaders and kidnets so simple because I just have to say treat these as JavaScript files and so I'm not even doing any massaging of what I'm loading in yet for anything in kitchen if there are markdown if I you know decide to put mark down Roots then maybe then I will actually use the full loader system and actually, you know translate mark down into something but I don't even need to do that. And if you've seen other Frameworks platforms out there like Swelled kit for example or swelled. Take a look at their tooling like the amount of work and effort that's gone into building the vs code extensions and this and that and whatever. Wow. It's it's it's complex stuff. Thankfully we just kind of skip over all of that because of some of the decisions that have been made in in the original design initial design. So here we are this is the index page. So let's take a look at what a page is in kitten. So here we're loading some Styles in dot Styles index.styles. So if we look at that, that's just again JavaScript that's exporting a CSS tag template. This is just CSS, right? That's what we're loading in and then we have our message component as well. Remember the chat dot socket is on the server. The index.page is is being run on the server but creating content for the on the client. Um, so then we also have a status indicator component. We'll look at that later. And then here we're again making sure that we have our messages table or initializing it if it's if it doesn't exist, that's a common pattern. And here we just export another function calling it root called whatever you like. We're not using the request of the response clearly. And we have some markup that we're creating. So here's the persistent kitten shot the title. We are instantiating the status indicator. That's where you saw where it was saying status online offline Etc. So we can take a quick look at that. So if I go over here This is a status indicator component and you can see that. It's got a couple of interesting things that it's doing. so here this x data brings us to the second framework that kitten supports natively and that's Alpine Js. So htmx is really great for doing what traditionally be like Ajax style calls and for responding to them in a declarative manner for doing web sockets with the web socket extension Etc. So for data exchange, it's really owned for that. What about more custom client site behavior in a declarative manner, that's where Alpine JS. It's tiny little framework comes in. So you can use kitten just it's basic HTML CSS JavaScript and then you can enhance it if you need to with Xh GM sorry with with htmx and with alpine.js. If you're not familiar with alpinejs. Just go to alpine.js.dev. And it is so small. You can just you can get started with it really really quickly. Again. They're loading it from CDN content delivery Network and then here's their example, so you create a model a data model over here. So in this case an object with a property called open, that's a Boolean that's false and inside of that so you can access that model inside of that note now here it says button at click. So this is an event handler open equals true and there's a span with which is only shown the content is only shown when open is true. so that's the sort of thing you can do with alpine.js I think there is actually a question. I'm just going to look at the comments. Sorry, what did I do? There we go. No, that is not what I wanted to do. There we go. That's a bit better. All right done. Yes. The green screen is not exactly perfect. I need to. Tweak it. That messages not component might be slightly difficult 3. Yeah. Thank you so much for that heads up. Yep. Let's see. Any particular reason why you went with htmx as opposed to any mainstream front-end framework or Library? Just curious since the project is so already so Jazz heavy. Yeah, I I like to think of the project as JS Lite because like yeah, there are heavy Frameworks out there. Definitely that we could have used and I was looking I was playing a lot with swells and with swelt kid Etc. They're designed with a very different mindset their design for big Tech. They're designed for Enterprise. They're designed for serverless. We are literally a server and I like small things. Which may or may not be come across and both htmx and Alpine I think are really just the right size for what they do. They work really well together as well and you don't have to use yet. Thanks sarcasi. You don't have to use them either like they're tiny and kitten supports it and what do I mean by supports it? So let me just show you what I mean by kitten support sets. So if we let's get the web browser out of the way. Let's look at the page. Okay, sorry. just over here. Let's look at the page at the end of the page. So I'm just going to go to the end of it. Am I in the page now? I'm in the status indicator components. Let's go to the previous one. Let's go to the end of the page. Notice we're creating the markup right this whole markup that I was showing you. And then notice here. we're returning from this route an object that has the markup the Styles and then it says uses htmx htmx web socket and alpine.js so when kitten sees this It ensures that those are imported. It serves them and ensures that they're imported on your page. You don't have to do this. You can do it manually. You could do it just like you so I can get it from unpackage if you want or you can use any other framework. So it doesn't tie you into anything. They're small enough. I think they're useful enough that there is native support for them. But feel free not to use them and also, you know, feel free to use anything else. It doesn't tie you it to anything like that. So let's just keep looking at the page. Oh, sorry. We were looking at the status indicator, right? Let's go back there. So let's go to definition open that up. So here's our status indicator. So this is all a combination of Alpine Js. And htmx so it's all declarative. What we're doing here is we're creating an X. We're creating a model an Alpine JS this you don't really even need to to use you could just write this like that that little JS tack template just gives me better syntax highlighting and I kind of like that. It's semantics so I use it. You don't have to um but we're basically setting a status property to initializing and that Status property is what the text of the node will be set to notice. I've already I've also duplicated here in case the JavaScript doesn't load or anything. You know, you just you know, that something's going wrong just sticks at that. It's not empty. Um, so then we're listening for htmexes websocket connecting event on the window. Okay, because it's actually It we are a component. So we were listening it globally. And we're just sending the status to connecting if it's connecting when it's open. We're saying it's online when it's closed. We're saying it's offline and that's it in terms of the class because we want to change the color of it. We're also doing a if status is equal to online then set the class to online status Offline that it's offline and there you just see the the colors for those so that's our status indicator component. And under that we have the main diff where stuff. happens so there we're specifying that we're using the websocket extension for htmx. And we wanted to connect to the chat dot socket that we saw earlier. We're also creating an alpine.js model that just has a property called showplaceholder true. And that showplaceholder true. Is this bit of HTML? Initially before anything comes in we want this no messages yet. Why not send one to to display? So that's that's what that's for. And then here we have an unordered list. And that's the messages list. and when the htmx load events occurs We're saying showplacehold. We're setting showplaceholder to false. And we are scrolling to the bottom of that message list whenever something loads. And then here's the body of it. so let's take a look at this. Can I get the whole thing there great. So this is and if he and immediately invoked function expression where we're calling this function with the messages in the database. So this is how the initial the persistent messages that we're already in there are shown and we're saying, you know, if messages length is zero then write out this placeholder. That we have otherwise map the messages to a message component per message. Right? So we're creating a message component for message. So let's take a look at that message component again. So let's go where that's defined. And there and remember it was just this right we were using this remember in the chat socket. That's what we're returning as well. So that's pretty cool. We're returning the same little snippet HTML. So let's close that and yeah. All right, so, that's our page root. What else do we have? We have some we have this index.js. This is just some fixes that fixes the interface on mobile tries to detect that it's mobile and then apply a few browser fixes to it and also enables htmx logger. You could just delete that file and It would still be fully functional. It's just some little tiny improvements. You don't have to worry about that really that's not important and then we have the index dot Styles which I showed earlier. So that is it. That is the whole persisted chat. Right? And I think that's also been a good introduction to kind of how some of the things work and in kitten but where we really want to get to is an end to end encrypted periods appear thing in the next 10 minutes according to our regular schedule. See how that works. All right. Let me see if there any any questions things always take longer than you think they're going to yes. It's true. Our causes says yeah. All right. Awesome. Yeah, no questions. All right, let's just let's go ahead then. So this is what we have to begin with. But remember that the persistent chat example was entirely open as well. It was entirely public. So let's start by implementing some authentication because when we have our intend encrypted chat, we want it to be, you know you to be the only person who can log into it, right and thankfully kitten has authentication baked in. Oh, right. See that looks like all right. So here we are. Let's get back to the terminal. And let me make this terminal as large as it can be on my tiny little presenter display. All right, so the first thing we're going to do is make sure that these files that you see over here can only be accessed if you're authenticated. All right, so let's just start with that. How do we do that? We have to write a lot of code. Not really. So what I'm gonna do is I'm going to create a root called private. I could have called this move the actual name of the root doesn't matter. But what does matter is I'm just gonna add a little lock to the end of it. Okay, I'll walk Emoji, huh? All right, and then what I'm going to do is I am going to take these files and I'm going to put it put them inside my private folder that I should I just showed you using it nice little terminal file manager called LF. All right. So now if we look at it, we just have this if we look under there there's the private folder and the files. So what does that mean? So now if I run this And I bring up my browser. Where's my browser? There we go. And I go to localhost. I'll get a 404 kitten poor little 404 kitten. That's because there's nothing in the root right? However if I go to private then I get a sign in. Ooh a kitten sign in how nice is that? It's asking for our our secret now when we first started it ages ago. It actually gave us a secret. We didn't know it down bad us. but not to worry. We could either go in and delete the database for this project right now and it'll be generated. We don't have to even delete the whole database we can delete the internal database to underscore underscore kitten if you wanted to or remember that the settings are generated per domain per Port per file path of project when you're testing locally to make it very easy for you to test locally. So what we can do which is much simpler is just to go in there and just to start this at a different domain. So while we're testing it, so let's do that. So I'm just going to go over here and say so I am in the demo folder yet. Okay, so I'm just going to say kitten I'm gonna pass domain equals. I'll say Place one Local Host. So kitten has support for four aliases to localhost subdomain aliases localhost Place one dot localhost place two dot localhost you get the idea up to place four and this is really useful when testing peer-to-peer web apps locally for one of the reasons is of course, they have to be on different ports as well. But kitten uses cookies for authentication and cookies are not isolated purport their isolated for domain. So when testing locally you actually have to use different domains or else the sessions will get confused and Each data between them. So because of that what we can do now is we can start actually start it don't we let's get the terminal back. So what I'll do here is say kitten - domain Place One Dot localhost, and that should do it. There we go. And now it's generating a new secret. Okay. So this time this is our secret you might be like, what the f***? Yes. It is Emoji. Um, why is it Emoji? It cannot be Emoji. It's never been Emoji before. How can it be Emoji Now the secrets have never been Emoji in the past. They cannot be emoji. They are Emoji now. So what is this? I love that every time I do something a little different. I think one of the first things that you get is like you can't do that. You can't that's not how we do things around here. Oh my gosh God. What's the world coming to? So it's a truly in the 21st century. I don't know whether that's a good thing or not, man. But yeah. um Yeah, so what is the Emoji that is actually your ed25519 private key encoded into emoji. That's what it is why Emoji just because it looks good. No because you can't write that down and if it was if it was a generated password, I know what some of you would do the get your little sticky notes and you'd write it down on your sticky notes and you paste on your monitor and then there's the security f***** right? So instead what you got to do is you have to select it and put it in your password manager like you should with all of your password, so I'm going to copy that. And then I'll my web browser here. Just gonna do that off screen in case I need to enter my password and do it again because I answered it incorrectly because of course and now I can bring it on screen. Go away. All right, so and what I'm going to do there is I'm actually going to go to 443 now. Actually, I'll add a new item. I'll add a new item. And I'll say password. Thought that that I'm using one password for this and here. I'll say a place one Local Host. persistent chat and I'll add this. And save it, okay. So now if I come over here. Let's see. Oh not a local host. I need to actually go to it from Place one Local Host. Right? So there we are Place one Local Host and I can just enter that password in and hey. Okay did not like it. Let's see. It just is that the wrong one desktop antenna can nope. That's the wrong one has it not synced to die not save it. So many things might have happened. Oh, you know what? Here we go. Let's just I'll just copy and paste it from here, but you get the idea. Okay, maybe didn't save it. There we go. And it just goes to settings right now from sign in if I'd actually gone and in your settings you can actually see your identity. Your SSH Keys generated a phpgp key is generated for you. You can see your secret there. This is very basic right now. I haven't really had a chance to work on with that will look like but if we go to private now, we can access the persisted kitten chat over here. Um, some things are not working. No. And that's because our URLs have changed as well. So whereas so we can't connect and so for wondering why that is then we probably have to look in our index page and we'll see that where it's trying to connect to is should be now private chat. Right? So if we change that then it should it should work. Let me actually just run it here and keep it running as well. So it automatically updates when we're changing things. So now we've changed it. So it's private chat. and let me Where's our? Oh gosh, okay. I should have really saved that. Password here. You know what I'll just I'll use I use my text editor as a very cheap password manager that you should never just for this just for this. There we go. And then I can just go back to the editor here. All right, let's paste it from that. Is it? Oh why my Local Host? That's odd. Oh, no. Sorry. It is Place one out of localhost. Yeah. Okay. No, it's going on. What's it doing? Place one not localhost Well, it might not running it in the yeah. What did wrong not do he didn't run it at the main equals Place one? localhost City Now we didn't. All right, did that work? No, because he can't spell domain. It's not the mine. It's the main. Now, it's Place one Local Host. Yay little victories. All right, and then we can sign me. Do not like you. All right, come on. There we go. They can sign in and it would normally just forward to hear because I was there but I refresh the page and that's why I didn't anyway. All right, cool. Let's get this out of the way. Slow but sure progress. In fact, let's get this up. Hi. All right, so What are we doing again? All right. Now it's authenticated right? Let me see if there any questions anyone who wants to. Tell me that I'm being sacrilegious. Holy pierogi. Hey, I'll take it. Oh, she was just to make secret more cute and less scary. But the idea that they aren't writable is great. Nice. It's not printable either. It's just too much color for anyone. Yeah. I know. I mean people will find a way right like they will they'll print it. I know this. I know it's Happen but yeah, cool. All right. Well that was the idea and you know, it doesn't hurt that it's also cute. Doesn't hurt. Alright, so we have authentication still a centralized app, but authentication and let's see if this works now testing. new one and go to Place one Local Host s privates Yeah, then also are all. Yay. All right, so that's working but Is it still centralized? It's not peer-to-peer and but at least not anyone can join it. That's kind of cool. All right. So how do we make it peer-to-peer? well for one thing it's not just it's not going to be a nickname and a message anymore. Right? We need a domain to send the message to so they're gonna be two domains. At least I'm gonna be on my domain you're gonna be on your domain and then we'll have the message and the message those not going to be in plain text. It's going to be encrypted Right, so I was planning. On actually going step-by-step in doing that, but it's already 6PM. So instead let me take you through the example that exists. It's not a lot of code to look through. so let me take you through it in a from where we are right now what we would do. Okay, so Let's bring up our terminal. And I'm going to go to kitten examples. End-to-end kitten chats anything running here. Let's just click that editor as well. Okay, and let me just start my editor here. To begin with now look at what there is here different to what we have right all this is the same. This is just what we did. We implemented authentication, okay? Now there are three extra files here. The index.html is just the basic is is just very very basic. It just links to the private root because like if you get there and you're not it, you're not authenticated. You don't know you want to go directly there. That's it's nothing. So that's just that's just for sure. um However, so we're now we have a peer-to-peer application and it's end-to-end encrypted. So for the peer-to-peer, what we need to do is deliver messages to another domain right and have other domains be able to deliver messages to us when we get those messages from another domain. We need to be able to let the rest of the application know basically the chat socket. We need to let it know that a new messages arrived right from another domain. So we need an event emitter of some sort. That's what we create in. The main dot script main dot script is in JavaScript, but it is a special file. That is run when your app first starts. So it's run by kitten when you're at first starts. So if you want to have something Global that's a good place to initialize it and that's exactly what we need for our emitter our events amateur for the remote messages and that's literally all there is there. We import event emitter from node events, and we just expose a global remote message emitter. That's it. That's all there is. Okay, and so let's take a look again at the last remaining thing, which is our inbox. so this is how we receive messages it's a post-root and that's why if you look at the Name of the file. It's dot post so this will get called when a post request comes in to slash inbox. Okay, so let's take a look at it. all we're doing really is we're getting the message as a request body. We're expecting it in Json. And kittens automatically doing that giving you Json there. We're just logging it out. And then we're asking that remote message emitter that we created emit a message event with the message and then we're just sending a very basic kind of because tutorials example, just an okay response. That's it. So if you call inbox and notice, it's not an authenticated route because different domains should be able to call it. If you call inbox and you send a Json format message that gets a minute now in an actual real world application. You would be doing a lot of validation here right to make sure that the message was what you expect. It didn't contain anything. You didn't expect Etc blah blah blah. We're not doing any of that in this simple example, right? So let's get back to sorry. um Looking at what's changed now? These are the same files but something's changed because now it's going to be an end to end encrypted message chat, right? So we said that when we get a message it gets broadcast. And that the chat socket handles that so we'll take a look at that. But first of all, let's just look at the let's go from the UI In from from the interface in So let's look at what's changed in the page? This is all the same. This is all the same. The names changed status indicator is the same. Here's that div, right? And here's what's different. So Well tooth no we'd actually change it to private. So this is the only thing that's different. We're listening using alpine.js to the htmx colon ws-config - send event. Where preventing? We're preventing the default action on that via DOT prevent. So the message is not sent. and instead A function that we've declared somewhere called encrypt message gets called. so that's the main difference there. We're not sending the message right away. We are encrypting it remember for end-to-end encryption. The encryption has to occur in a space that you control. You. Don't that's not the server. That's the client for you, right? That's where your secret is. You also have to understand the security properties of what you're building. So kitten is a web server. This is a web app. So this anything you build here. So this this little example of course don't use it anywhere. But once I build some of this stuff in a much more production ready way in kitten itself, and it can be used by a kitten applications. It still doesn't change the fact that there are certain things that it won't protect you against. so we're using https already to speak between kitten instances between these domains. So your messages are encrypted in transit anyway. And to an encryption really is useful for encryption at rest, right? So on the server they're being stored on the server. So what does that protect you against? Well, let's say that this is hosted at some VPS provider and some admin there, you know likes to look around they have access to your server. They look around they don't see much right now if they have the time and the inclination to actually run something else on your server that you then connect to while you're authenticated and then they get your secret. That's what doesn't protect you against so you have to understand like what the security properties of any system is web-based system basically means that if a different piece of code is run on the server at any point, it is just one request and yours your key could be compromised that way your secret could be compromised. So what kind of a threat model with this kind of entail in terms of like who would be at risk of this? I'd say someone who's being targeted directly for surveillance someone. that you know, it's a state-level actor could maybe get access to to a VPS provider remember also though. These are going to be spread out on different providers. So, you know that actually makes it less likely that that's going to happen unless they whoever is after you manages to you know hack. Has all of these ways to hack different providers all around the world. So that's that's a bit where it's better than a centralized system. But at the same time, you know, your VPS provider or whoever else could be easier to hack you could be running this on a tiny little single board computer attached to reuter at home in which case if the physical security of your home is compromised Etc. So, you know as a good rule of thumb I don't use this if you're doing like Edward Snowden stuff. for a very long time if ever but for everyone else the whole idea with encryption in general is to raise the cost of surveillance to raise the efforts of surveillance so that hopefully it's only used when it's really really necessary in a targeted way and again, You know that that whether or not that's a good thing depends on how much you trust the people doing this surveillance, but that's way better to master surveillance. And that's really where you know encryption is useful for raising the cost of that. So understand that nothing is is secure 100% You have to understand if it's secure enough for your needs. So wow that was quite a disclaimer, but I think it's important to understand that, you know, because like and if anyone tells you this is entirely secure they're bullshiting you. Right the same issue that exists with web applications exists with Native applications as well. Like you know, it only takes one update of an app in order to compromise your security if that update has been compromised either through a supply chain attack or you know, the provider or the App Store being hacked or the binary being hacked if it's a distribution, you're not getting from an app store Etc. You know, if it's a centralized system you have to actually trust that the people running your messaging application are not interfering with the signaling that they're not adding another and to your end and encryption chat that you're having on the notes to you. So, you know again, I think the great takeaway from that is from this is nothing is secure. Is it secure enough? Do you think for your needs and how do we like raise the cost? Of of surveillance anyway. So all that to say that's what's changed here. If we look down here nothing else has changed my goodness. Look at that. Aha, the form has changed a little bit. We have a two instead of a nickname and that's a domain and here we have plain text. Is the name of our form field just to make it clear that this is not something we should be sending unencrypted as the plaintext. That's it, though. Nothing's changed on the page. We're even using the same message component. But is it the same message component? We'll take a look at that in a second. First of all, let's look at the other files. What's changed in our chat thought socket? So in the chat thought socket. We some things have changed. a little bit So let's take a look at what that is. so here's where we're creating our sockets root. And it looks like we've refactored descending the saving and the broadcasting of the message into its own anonymous. A closure here. And the reason we did that is because we need to call it from two different places now, right this is doing exactly what it did last time. But we need to call it from two different places where well once when a remote message comes in right? So we're creating a remote message Handler. And listening for that remote message and emitter message event. When a remote message comes in. We validate the message and then we save and broadcast it locally. So a message come in. So we've broadcast it to any local clients that are that are there and then we do a little bit of housekeeping here if the socket goes away. We also turn off that message emitter. And then here we just have the socket message event that we're handling as well and we do it there. So there we get it. We just what we're doing before. We just call that refactored method. And then here's what's different. We need to deliver the message. So this is a local message. So we need to deliver it to the remote and here we're just basically string creating a Json string of the message creating a post request here. and sending it to the inbox of the remote. The remote domain the remote small web domain. That's it. The rest of it is the same. And then so let's take a look then. At this message component because that's changed. It's still a list item. It's still sending you HTML, but notice that we've added some new alpine.js handlers for clients side processing. Remember this is sent from the chat socket as well. So when this list item is loaded. We have this. We have this message text property that's sent to the that is set. Sorry to decipher text. So that is the encrypted text because the server does not have the unencrypted the plaintext right? So that's set to decide for text. That's all we have on the server. And when this note is initialized we're saying okay decrypt the message. And we're sending the cipher. We're giving it a deciphertext the message from and the message to Right and then it just here displays message from message to and here dynamically binds the contents of the message to the message text property in our in our model up here. So that's it. A list item with encrypted text loads into the client and then when it loads it gets decrypted and displayed that way so quite elegant. So let's take a look now finally at what's actually doing the encryption and the decryption. Um, and so I'm going to open up the the index.js file. Remember previously. This was just used to do some that's still here mobile interface improvements. Right like we're doing this stuff but we've added about 30 lines of code to do the encryption and the decryption. So, how does that work? well for one thing it's 30 lines of code because Kitten provides a cryptography API, right? So that's what we are importing here encrypt message for domain and decrypt message from domain. All right, so then we're creating an encrypt message method. And this is the one that's called. If you remember from the page before messages sent remember, we're preventing that event and through htmx and calling encrypt message. So we get the parameters from event detail parameters. We get our private key from local storage. It's our secret that was added there by kitten when you signed in. And then we call encrypt message for domain with the plaintext that we got from the form our private key and the domain that we got from the form, right? And then what we do is when we got the encrypted message, we manually use the socket wrapper that sent to us. And it's sent function to create a Json message that we send to. To that to that to our chat socket. And when it gets it remember it delivers it to the remote and we saw that. So with our ciphertext the from we're just using the window location host and to parameters domain, okay? And decryption is similar. But even simpler here, we're just saying if it's from us then we need to still get there public key. So we flip that I could write that code a bit more. The intent could be clear there and then we get our secret and we say decrypt message from domain with the ciphertext our private key and the domain that we want to get the public key from now, let's go one step further and look at these encrypt message for domain and decrypt message for domain functions. I don't think I'm going to be able to do that from here. Can I actually go to definition from here? No because it's in there so I can show you that. in kitten proper so if we go to the crypto Library crypto API and let's look at what we have here. We have decrypt message from domain and we have where's encrypt message for domain? Should be somewhere anyway. Let's look at there's encrypt message for domain and here's decrypt message for domain again, quite simple. Both of them are and getting a shared secret. So what's happening here cryptographically is we are using it diffie-hellman key exchange. And so we are calculating a shared secret using our private key and their public key how we getting the public key their private key. I'm sorry. They're public key what we're doing. Is using another property of kitten. Which is that if you go to any kitten site and you go to forward slash ID and kitten is running which is really important. That kitten is running. So let's go over here. Go to end Okay, and let's just make sure kitten is running here. Okay, so when kitten is running let's go back here. If you go to forward slash ID. you will get that notes at 25519 public key. All right, and so that Is how we get the public key of the other node? So, let me just go back to my terminal. Alright, I'm just going to note this down as well because this is going to be useful for us when we actually see it running. So I'll just note that down in my hastily made password manager / textpad. Okay done. All right. So let's go back here. So we're getting a shared Secret. And then we're encrypting using that shared secret and returning the ink for message and we're doing basically the reverse of that in decryption. So shared secret for domain is doing the heavy lifting. If we look at that. We basically have an object that keeps our shared secrets. For different domains just for caching. So we don't have to get the public key all the time every time and then we are getting their public key response with a fetch call and then basically converting our Emoji string to a secret to a wide array that we can use and then we're just using get shared secret get shared secret itself is part of the Noble at 25519 library by Paul Miller tiny cryptographic Library, so I didn't roll my own encryption, which is very important. Don't do that. There are you know auditable libraries like this very very small multiple libraries that you can use. So that's where That yes, so that's it. Really we will go to any more detail on that. You can look into the noble Library Etc if you want more details on that and let me just this. Okay, so let me just see where my that was kitten. Okay, right. So the end to end encrypted charts example, we're back there and I think we've covered everything we basically Yeah, we've covered everything. These are the same the message component. We saw status indicator hasn't changed to check socket. We saw we saw that the encrypt message in secret messenger there. We saw the page. That's it. So we know how old this is. So what does it look like when we run it? Let's do that. So we're already running a place one Local Host. That's great. Let's keep that there. I'm going to run another one. So let me go to end and I'll say kitten domain equals. Let's do place two local hosts, but I also have to specify a different port so I'll say Port 444. Okay, so that's generating a secret as well. so I'll just note that secret down in my little whoa. What is that escape? That's not what I wanted to do. Is not what I wanted to do, there we go. All right, so Let's get rid of this one. We don't need it anymore. This is our place one. This is our place to Secrets. Okay? And we'll keep that there. So now what I'm going to do is open to web browsers again, it's all about the two web browsers. And one I'm going to go to place one Local Host. And I'll sign in with my first secret how many things can go wrong right now? Yay. Okay and 10 encrypted kitten chat. It's online no messages yet. Perfect. Now we'll open up another web browser window. Over here and we'll go to place two dot localhost. Colon. 444 and actually to the private route there so that helped there and I'm going to use my very basic password manager here to enter my password there. All right, let's get rid of the passwords. Huh? So we have two two instances of our application running. So let's try and send something. So I'm going to try and send the message to place two dot localhost 444 now normally this would be a domain so you'd be saying I want to send it to a roll because that's where it's running right or I want to send it to Laura calbag.com. We're testing it locally. Okay, so we're gonna say place to thought localhost 444 And as my message, I put two dots yeah as I message. I'm going to say hello. I'm gonna send it. And look it's command did you see that? There was a little flash as well there. Well, it was being oh go away. What are you don't go away too much. I think you're the server. There's a little Flash and it's decrypted. So that's pretty good. So let's just do the reverse from here to place one dot localhost. It works. woot and there we go. Hey, it works woot and come in over there. So if we of course look at the Let's see where my my terminal. There we go. I think. Think we can shut it down. Now. You've seen it working. Let me just open one terminal window. It's easier. There we go. If we look at the databases again, so let me just look at databases. And this is projects kitten. Let's see and and decrypted. Okay, so let's look at the one for our antenna kitten. Let's see. Which one this is. Wait. Yeah, and there we go. You can see that what's here? Sorry for this. It's I think my green screen is not holding up. Let me just see if I can change the key Edge and make it a bit better. Nope, that's not helping. Maybe the background to do no, that's not healthy the foreground. Ah, there we go. That's a bit better. No, is it better? I don't know. It's gotten rid of. A lot of the color there, though. Maybe the key no, no. Real time green screening here people. It's amazing. Things you get from these streams like okay. Let's screw it. I think we're just gonna have to live with a little bit of you know us that was whoa. Oh, that's my that's that's my switcher. That's what I was using I go away. Go away. It's all going wrong. Anyway, so here you can see that it's a ciphertext that's being saved in the database. And there you go and to end encrypted to peer to peer small web application basic example, but this is the direction that it is going. It's so nice to finally be able to work on stuff like this is a lot to do there's a lot I need to do you saw that remote message emitter, you saw the inbox. Those are going to be moved in but implemented in a production ready manner in kids in itself. So any kidnap will be able to use those. There's also going to be For public messages, you'll be able to make websocket connections and get public messages that way as well between notes for following basic. That's how following is going to work. So and these are slowly I'm adding these slowly to an emergent sort of a small web protocol. So let me see if there are any comments done. Um our case says love it kind of scared of all the corner cases. I'm sure I am too. I know Corner case of the bane of Matrix element, but at least your use case of some well, that's the thing right? It's about simplicity. So, you know, if there is if there is an issue, it's gonna be very apparent. It's gonna be very clear because like there's so little code and that way we can actually you know, and that's true for security in general as well. The less code the better. So Ben at Mastodon dot bits -unt - baume.org says so my client connects to the other party's kitten to communicate. Yeah, so to the other party's app made with kitten if the app is a peer-to-peer app, like we just created so part of what I'm trying to do is kind of share every piece of what I'm building. So all the way to you know, what does the automatic Local Host certificates handling the automatic, you know, let's encrypt certificate handling. Those are all different packages. You can use those you can use kitten to build different apps. So I'm going to be using kitten to build my kind of app for being public and private on the web which I'll probably call someone like place and people will be able to one place where you'll be able to do like private chats like this and then post public things and people will be able to follow you but other people will be able to build other apps. And I think so. That's what's interesting to me keeping a very very simple protocol very extensible protocol at its heart a very simple way to be private a very simple way to be public for people to be able to follow your public posts, the fundamental assumption that all of it ties down to is that we will all run our own servers and then of course the question is how do we make that process as simple as possible? And that's where domain comes into it which is a hosting tool that I'm building so that anyone can become a small web host and host people on vps's with a 30 second 40 second process for actually getting set up that requires. No technical knowledge. That's itself being written in kitten. And now that I've gotten some of these things out of the way. I'm going to focus on that again. Does that mean every kidnap needs a different address Port every kidnap when deployed is meant to be deployed at a different domain? So yeah, so the the level where I see them being deployed is a very small vps's or tiny little tiny little single board computers at home. If people want, you know more control over it. So I hope that makes sense. But that's the model. So we're going with like radical Simplicity. We're going with because I think that's our advantage as well. That's you know. Complexity is Big text Advantage because of the barrier of Entry. Both monetarily and in terms of effort Etc. So we're the opposite of big Tech with the opposite of big web. So we Embrace simplicity. And oh cool. Yes makes total sense to me. Sounds great. Awesome. Folks I'm not even going to give out the studio URL because at this point it's been an hour and a half and I hope you found it interesting. I hope you found it useful. This is very new like I got this example working this week like two days ago or something and so kitten is very rapidly evolving. full small web protocol spec when when No, you can our Causeway or arcades are causing you can you can build stuff with it now just play with it. If you want to the full spec. I don't know. It's not going to be a very full spec. It's an evolve. It's going to be an evolving spec like the spec right now has for example slash ID for getting your public key from there. It's gonna stay very small. So it's going to evolve. I want to see it evolve with usage. I want the core spec to stay as small as possible to be as extensible as possible. I want people to create their own vocabularies. I want the people who use this to create the semantics using it and so it's it's not like it's not going to be like an activity Pub style thing where it's this huge Speck and you have to change the spec every time you want to do something different with it. Now, it's the opposite of that as well. So I'm full spec when I have no idea watch this space. I'm working on this every day. This is what I'm doing and and You know start playing with it. If you want. You can't deploy yet the deployment stuff when it does arrive is going to be like inside Jazz. It's going to be fully automated. So it'll be automatically updating itself automatically updating applications. Unlike site.js it's using git for a lot of that. So yeah. right And I think this is a good point to leave it at if no one else has any questions. Yeah, waiting one to three. All right, cool. You know what? Thank you so much for joining. Thank you so much for your questions. I'm sorry. I wasn't able to give out the studio URL and have anyone like, you know on video chat to ask questions or whatever. It's just been a very long stream, but I think we covered a lot. You're very welcome Ben Harold. Thank you so much. And if you guys have any questions, thank you our God say if you have any questions, I'm on Mastodon. Mastodon.ar.a.l. Feel free to hit me up there or you know, I don't know if a cowork doesn't have discussions does it now? Oh, yeah, you know, you can find me that find me on on the fatty verse and thank you so much for joining and I will see you next month at on smallest beautiful if not before. Take care of yourselves, bye-bye. And where no, we're not because there's a confirmation and where