Good morning folks. Let's see here. Dustin. Hi. Let's see here. A little chat column agenda. And I seem to have way too many windows open already. But I guess that's what hibernate does for you. Hey, Derek. Okay. I'm just pulling up the latest. So give me a second here. Okay. Move the cheese. Okay. So. We'll give a minute for folks, but basically. Just trying to make some progress on the workflows so that we can. Get back to testing the notary clients. Hey, Omar.  I'm just trying to make some progress. I'm just trying to make some progress so that we can see you. And that is your new place with a very cool sliding barn door. Ha. It's correct. And it hasn't fallen down yet. So this is good. That's a, that's good. I guess you put it up yourself. Is that what the lack of endorsement there? Correct. Yeah. I've done some myself that is quite the challenge to. Yeah. Yeah. Yeah. Right. I'm just rambling. I was giving a couple of more minutes, but. So, okay. All right. So if you remember, if I get the workflow that we're trying to. Be too. As we're trying to get this experience kind of sketched out. So we can make sure that. We can make sure that we don't get too much of a glow going on. So we can get the end end workflow. So if we can get that sketch, we can see like, all right, does, do we like this experience? Like it's the, you know, putting a little bit of sample paint on the wall before we tape and mask and paint every wall across the house. Like, do we like this experience with the spotter of paint? So we're, we've got some basic signatures. Like if you look, you know, kind of, well, that's the most important thing in the process. So that we could be able to work with. And, you know, we can, I know how to generate, for a container image with manifests. We've been trying to figure out, okay, if we have that, and we know we could sign and verify something. Can we get an in and out of a registry so that we can promote it across two registries. And then use a policy manager like Opa. Which of course means it needs to be able to pull something from a registry and validate and so forth.  You know, I've been making some progress on that and we will fold that in. So there is an assumption that keys magically wind up on users machines. And we need to figure out the whole key management revocation all the pieces that relate to that so we're kind of just ignoring that one for the now for the meantime. We're ignoring it we're keeping it in a parallel effort and we'll come back to it. So with that's the case, the thing that we've been discussing is how do we persist signatures, how do we discover a signature and based on discovery what is this persistence metadata. Like pulling a signature is pretty straightforward because we're just trees and artifact. So just like you can pull any other artifact you can pull a signature object. And then overalls been doing some stuff on like a reference implementation with distribution. And then there's this prototype which you'll see is exactly why we need to work with some other clients because this is splitting atoms. Okay, which some will find cool because splitting arms is cool. So the, if you remember all this. So two things actually, where did I put this alright so over the weekend. Well that's unfortunate. Oh I see what happened. Okay, hold on. So over the weekend. I moved the discussion we had around different options for persistence. So if you remember we talked about a manifest index or a manifest link through index. There was a couple of discussions and how we decided what we want to consider. It turns out we had both the options and the current implementation in the same read me file. So it kind of really made it confusing for those of us that are directly involved much less anybody else looking. So we've split this out. Now in the docs distribution for envy to there's this persistent discovery options, which talks about what we've done. And then the root read me distribution read me will have whatever we're currently thinking about. And remember we've got this under this prototype one branch, you know, assuming reverses and so that's kind of just a little bit of stability of what we're trying to show here's the thoughts. And here's what we're currently using as the model. So with that of the three options, we've been sent doing around. If I have the net monitor container image. And in this case, it's, it's a matter of you'll see the 111 and just says it's a manifest. I'm ignoring the platform for the simplicity of this conversation. So I've got the image and I'm going to push an index that has a config object that has the signature in the content of the signature content of the config object. So in this case, if you remember the workflow, we've got, it comes from rabbit networks, because they're the ones provide authoring the software. I'm skipping the signature and Docker hub just for simplicity and eventually it winds up in Acme rockets, and they're going to sign it before they deploy it. So here's the web of networks. It eventually gets into the Acme rockets registry and Acme projects rocket signs that content. So that's the persistence model. And for those that are more text versus visual, I've put all the samples in here. So here's just a manifest for the net monitor image. And then here's the rabbit network signature. Notice it's an image index. I, we should talk about the version in here so long to make a note to come back and talk about that. The big media type says, Hey, this is a notary v to JWT, and it's signing the rabbit networks image. So those two match, right? That's that thing and nothing. And then there is the additional one. Oh, and then this is the signature itself in the config object. And the Acme rockets version is duplicated. So we talked about the pros and cons just to and then the expansion of that if you do use a multi arc index, how would the signatures look for that. So if you have your windows, notice the W for W manifest and then the Linux manifest. These are two individual manifest that are referenced by multi arc index. You could have both the Acme rockets and web of signatures on the multi arc index. And you could also have both of those on both the windows and Linux architecture specific manifests. And so far, just close the door here. Is that just going to raise kind of caught up in the recap of where we're at. So I don't quite understand what the different. So these are all the different nine different artifacts here are nine different Jason blobs. These are nine different artifacts. These are two to these three of the things we know and love today that are already out there right it's a windows and a Linux manifest that represent the net monitor software, and that's a multi arc index. These two, and these four are signatures on those. So these are the new things. Okay, so there's a, the OCI index pointing at another OCI index in this case. And OCI links can point at a manifest, which is what it's doing here, or an OCI next can point to another OCI index. Yes. Okay, yeah, I was just trying to understand what's going on here. Okay. No, it was a great question somebody asked this so I added this to make sure it scaled. So we're finding interesting holes in the spec that don't actually seem to refer much to indexes or even manifest referencing there's a bunch of implications there which has been interesting to surface, which you'll see the talk about it right here, but it just a level set so we have basically this. And this are the same thing just expanded to show a multi arc scenario. So then, if we've got these things persisted, and we're following with the model that a deployment makes a reference to a tag or a digest. And we want to be able to not, we don't want to force people to change that in their kube deploy compose, tell them whatever. But the environment will say well I'm only going to allow deployment if x company has signed that content. The idea is that I know the thing I'm trying to deploy I don't know what the signature is. So can I get the signature objects for the thing that is being referenced. And that's that's kind of what we're for focusing on here. So the idea is I need to be able to say for a given digest the net monitor software, can I find out what signatures supported. Now, we've been having this healthy conversation around do I have to make any changes to distribution to support signatures, like, well, that's a reasonable question. I believe that the implication of notary v one was a really heavy weight solution that required people to implement to support signatures, but we don't see a way to have no impact on registries to support signatures. So we're trying to balance that with what we think is the most reasonable. Other questions came up of, why are we special casing signatures. Should we just implement a pattern that can work for any artifact type. So that's the thought process that we have at this point is a thing can reference other things. It's actually seen it's actually somewhat interesting to say hey given a particular manifest, what indexes are pointing at it, like that would actually be kind of helpful. And of course there's other scenarios like seen abs and others that will use indexes and I hadn't thought all the way through how an S bomb might be referencing other things as well so there's probably a number of good use cases that will come up where we need to get this reverse lookup model. So the proposal basically is, you know, for an artifact for repo, the manifests, and this is kind of the standard thing is there's this new API that says refer metadata. Lots of conversations on naming, but let's go with the concept for a second and we can have lots of fun debates on the name. So if we use this diagram above here. If I know this is what I'm trying to deploy. I don't care whether you just the windows or Linux and notice it did at least get the two right. What are what are the signatures that are available for this. So the idea is you make a request for that digest. And of course there's the tag to digest, you know, calculation that's already done.  So this just, you know, duplicates this just says hey this is the thing you asked for which looks like I actually did that get the copy paste right this time. And here's the descriptors that come back. Interesting problem is because the way we're trying to the way OC artifacts works is we're really just leveraging the scheme to scheme is that registries already know how to persist a manifest which has a collection of layers, and an index that is a collection of manifest or other indexes. So this doesn't change those two schemas in a registry. All we do is say the collection of layers or blobs that you're persisting could be something else, and we want to know that they are something else. So if you look at that you realize that on a descriptor, the three elements will always come back as index or manifest. And if I get a collection back that says here's all the digest, here's all the things that reference the net monitor image. I don't know whether I'm going to go through this list and have to find out, I have to pull it first to find out oh this thing's actually a signature versus, you know, the other thing I actually care about or in this case. I want to get just the signatures.  I really just want the signatures. So what you're seeing a start to tease in, and I'm not overly thrilled with it but I'm putting it in there for discussion is we return the artifact type, which is the config media type. I noticed now I can say, oh, of the things that I just found for this thing. Here are their two signature objects. I still don't know which is the web at networks and which is the net monitor. Acre Rockets one, but at least I know it's these two and I could ignore this one. I'm going to pause there for a minute. So this is basically just a filter out the refers that might have a signature. Correct. Well, in this case filter in because I want I really was actually two parts there's actually was in fact the current prototype might have it a way to specify the media type on this request that says only give me certain ones. The problem is this is not something you put a URL so it has to be encoded is it do we want to put it in the header. So, and the whole question of filtering always raises a lot of questions. So for now we're just saying let's start with refer metadata API that brings back anything that was referencing that object. And then the client, whatever the client is would say, I care about these two. Let me go fetch this object, then this object, and decide what I want to do with it and this object, because it's this type, I'm not even going to fetch because I'm not interested. So it's still two hops though, or two more round trips to get the to get at the signature for your quest total because there's to the refers and then you have to ask for the index and then you have to ask for the signature. So let's let's say we have a tag, because that's what you know we kind of want to say is the best practice for deployment use unique tags for deployment. So then I take from the tag I have to go get the digest. And then from the digest, I can go get this refer, and I lost track of many. So I started the tag, I get the digest. So I make a request for a tag to get a digest that's one. Then get the refers. And now I've got this list so that's two. So now I have to ask the two additional ones in this case so that's four, right, because I'm making a request for these two. And there's another hop in there which I'm going to work for a moment to figure out that I actually only care about the Acme rocket signature, because I only allow deployments to be done if Acme rockets is on that object. So that's five. What's what what we save though is I didn't have to go fetch this just to find out I don't care. Yeah, so I mean I agree with the with the data model being proposed here just trying to figure out. How to optimize this because in some cases like yeah you're saving something but if you're going to have it in a specific endpoint that trying to optimize then it should actually be optimal not just like save one extra hop out of like make it save off like 25% of the hops or something. Yeah, there is you're going to see there's an interesting config versus manifest reference that comes up as well in this discussion and we're definitely trying to figure out what's the balance between purity, and which is very generic use for lots of things but maybe it's going to be performant impactful that's not interesting versus they, you know, where's the right balance. So I, we're definitely struggling with that conversation. Yeah, I've come I've come in here late because I think the last time I joined the meeting. Yeah, a lot about the data model and I think the data models right in the way that it's referring to now I think it looks, looks good. This stuff you're thinking. Yep. Okay, cool. And Sam's been asking some really good questions it's work making us kind of test that. And I feel the questions they're being asked are keeping us in the right center. What, what kind of questions. And Sam remind me that what I know you're asking around the, oh, you're asking about adding to a layer to manifest or something. One of the big questions that I had was, why do we really need a new config field in the index. And are we able to leverage the existing manifests array. Instead, with just a separate media type for the reference that's controlling where the signatures is talking about the, the thing that it is signing. So if we, is it this one? Yeah. Persistence. So if we look at this one, what I think, Sam, keep me honest, I think this is what you're asking. If I have the net monitor image, what I would have is an OCI index that is the collection. And this is the rabbit signature, but instead of storing it in the config object, I actually store the signature as another artifact over as a manifest, which has a layer that actually the layer that's the signature. It doesn't actually have to be a manifest object. You could use the manifest field in the index to refer to it have a descriptor for pretty much whatever. I'm just pointing directly to a signature object in there. The question was just, do we really need to introduce a new config field into the index or not. So I try, I think in here, I went through that try to give some conversation. Not new changes. So discussion, discussion. Okay, I'm getting. So it's in that, in that poor quest. It's like one comment above the one still expanded. It's the one that's, yeah, that one. Okay. So if you look at the example here, I have, I put it so that there's two things in the list of manifests in that manifest array in the index, one of which is the manifest of the signed image and the other one is the same thing that you had had in the config field. Just moved it down into the manifest. And that was the question, do we really need to introduce and do config field into the index or not. So the question that this kind of teases that so this I might have wrapped up multiple times we were trying to first do this because there's multiple ways we refer to schemas and manifest. This media type is on the two schemas that we currently support the registries currently support image manifest image index. So this is saying is, it's not the config media type but we're actually going to say, registries will actually support yet another schema, which might be the same schema, because this isn't the I think this is, I think I'm not understanding you and you're not understanding me. Okay. So, in the example that you have in the doc. You have this exact same media type with that same digest and size, but instead of being in the list of manifests, you have it in a new field called config. Right. And that's all of what my question is, why do we need a new field called config versus moving that into the list of manifests right here. Fair, fair. So, I hear you, I think I hear you, but let me try to echo back what where what where I'm seeing it. First the index to have a config. Also, whether there's a storage in the configure not helps us understand the signal the index is of type signature. So that's the first part. The second one, which is actually the more important one is a list of manifests technically only supports today, two different media types. The image manifest and the index manifest or image index or image index and image manifest. And those represent schemas for how an object has collections. If this here would to have it here as a descriptor. If I understand the scriptures right maybe I don't understand you right is saying that there is a different type of schema that we're putting in the manifest collection. So there's like two statements that I want to want to tease out from what you just said. One is that you said that the index only supports two different media types in the manifest. Maybe I'm missing things in the spec but I didn't see that restriction there. So that's one. The second is that you called this a signature index. And I don't know that we need to have typed indices the type indication is really the manifest type field itself that's telling you what you're looking at. And I don't think that we need to have it like a separate signature type of index versus an image type of index. They're both just indices. And you look at when you're an application and you're trying to interact with this you read the media type field to figure out what you're trying to look at. Let me deal with the index as a type separately, because this is the one that I think will if we can fix this it would make a lot of optimizations better. So if I look at the image index definition, which is ironically not in distribution but in the image spec which is the refactoring conversation we've been having. So it's an array of objects that are committed manifests and it talks about these two. Basically this is the image manifest and this is the image index. In fact it says yeah but those that's not a restriction right there it says it must support at least, which implies that definitely could be other things it could support. So here is, so this is something we discussed early on artifacts and I'm really happy to have this open this discussion again. The, if I think about the way registries parsed incoming manifests. What makes artifacts really easy for registries to support is we actually didn't ask them to change anything in their incoming of parsing. We get a manifest that references some layers. The fact that the layers are is the one is only one layer and it happens represent a helm chart or a foo file a markdown file is irrelevant it doesn't matter. The only thing a registry has to do is say I allow other config media types to be persisted to be submitted as well. So that's what makes it super simple. The two, and we haven't dealt with index yet right this is all new and OCR effects doesn't support index yet the that same schema processing is the processing that gets done on an index as well when you look at an index. There is a schema for this index that is described as. And in fact this whole goo for the multi-arch step is all optional. So you can legitimately ignore this when I'm trying to persist. Let's just say a CNAB. Let's take signatures out for a second because it's just loaded with all these conversations. A CNAB is a collection that has an invocation image, which is supposed to be how I run this whole thing, and a collection of other images that might get deployed. In, if it would, if it worked better, it would actually have the helm chart as well as your compose whatever else that the CNAB needs. What they're doing today is they have to stuff those additional artifacts into the invocation image, which means that every time I want to deploy a different helmet I have to rebake my, my invocation image which just feels broken. So you're building your CLI because you're trying to deploy a particular thing. So having that collection is really helpful to be able to say that there's a collection of something else, not a multi-arch image. But I wound up going on the different artifact types for index. What I was trying to focus on is a registry doesn't need to, that knows how to parse indexes and index doesn't need to know these, these two would ever be any different. And if we do say a manifest media type is different, how much more does that make it complex for registries to parse? So in your proposal, the registry does need to know about the signature, because the registry needs to figure out how the signature, sorry, I miss speaking right now. But like, I don't think this changes much. Either the registry does want to parse it or it doesn't. And if it doesn't, it can treat that media type, whether it's in a config object or in a manifest as opaque. I don't think it makes a difference to the registry, whether it's in the config field, which doesn't exist yet, or if it's in the manifest field, which does exist today. The manifest does exist today, you're right. We still want to, which thread do I pull on, what it's still, let's say we go with this proposal. I'll take, let me use this one here. I'm suggesting you still want to push the signature as something else, because we always want to have a mutable objects. We don't want to have to, if I'm pushing a second signature, as in this case, I don't want to update this index. I want to be able to push, like when the, when web networks offer authors, the net monitor software, they might push it and immediately push the signature, that's fine. But when it goes to Docker Hub, they should be able to add a signature without changing this one. And when acting rockets wants to pull it into their register, they should be able to add a signature without impacting this or the other one. So Steve, I'm not suggesting a change of anything else in your proposal right now. The only thing that I'm asking is, why is it a config field versus in the list of manifests. Because you can still have the exact same thing where you've got a separate index that just pertains to the signature and the reference to the, the image, as opposed to the index that is the multi arc image itself. I'm not saying that we combine them, like, given the structure that you've laid out in this document here, the only question is why is it in a config versus in the existing list of manifests. No, great. That's what I'm asking and I'm just trying to, I'm just trying to get a solid base. So if we agree these are separate, then that's good. And then the question is, because now I don't have to worry about this being a multi arc index, right? It's not a multi arc index, it's a signature index. And now we could say that this has two references. It would have the, where's the manifest. Well, I guess that would be your suggestion here. It would have, where's your change. This is yours, right? Yeah. So it would have a reference to the web networks image, and I would have a reference to the signature. And instead of it being, the problem is this is a blob reference. It's not a manifest reference. But this is a blob descriptor. So it's the, I'm trying to understand one thing though. So is the point of this config field and using the index in that way. Is it for backwards compatibility or what's, because like, yeah, what what sounds proposing would be compatible roughly with existing registries today, the config method would not be compatible with any registries. So I guess I'm trying to figure out, like, what is the compatibility story and Sam, is that your, is that your main concern here is that if we introduce a new index type that there's to be no compatibility kind of on the day of release. Yeah, I mean, that's, that's a concern. I don't know if I'm, I'm, I have an opinion on whether we should or should not have the config field. I'm just trying to understand, do we have a more fully articulated reason for it. So that there's two parts because they're pulling at each other. What's nice about this, because we're bouncing with it back and forth, which is fine, is this does make it more optimal on the amount of round trips I have to make. And it also says that this config object, which actually storing data is a little weird, right config should be some kind of configuration information where the signature is clearly data. So there is a conflict that we're doing. The reason that we're putting config in the reason we're proposing putting index, sorry, config an index is twofold. The primary is just so that I can know that this index is of type signature. Right, so I can say actually it's probably easier to look at this one, because this index is a multi arc index. An index is a signature object. So we're basically saying there's different artifact types. There's container images, there's helm charts, there's wasms, regardless of where they live opas and so forth and those are all manifest types. What we're saying is there's also some other artifact types that that makes sense to be represented as an index and a CNAB has been the most obvious one. And the idea is that a signature can be one as well, because it allows me to do a reverse lookup. If we come up with, because even if we're saying that the signature is in here, and that's why I was trying to just focus on this one in that second ago, was we're still suggesting that a signature is of type index. We're just saying that instead of being put in the config we're putting in the manifest list. But if I query a registry for the objects that are in it, I'm going to want to know that this is a manifest, sorry, a container image. This one is a multi arc manifest. This one's a signature. This other thing off the screen is an opa versus a wasm or whatever. So if I don't have a config object that says this is a notary V2 object type, then how do I know this index is not a multi arc index. I guess, if we're talking about, I think we had this discussion, I don't know, I don't remember when the discussion was, it was the last notary call about kind of shoe horning the config into index and my opposition to doing that. I think that's kind of related to Sam's underlying concern is that it really adds more ambiguity and confusion to something that we've already, I mean, we spent the last year to complaining about the confusion around indexes image indexes versus artifact index versus manifest versus artifact manifest or just the whole image versus artifacts and adding config here. I don't think it's actually the, I don't actually think that fixes this problem. I think it actually makes it a little bit more confusing because of backwards compatibility issues. So let me ask more basic question because this is, you know, sometimes you have to go forward, take a step back and re-evaluate. We've always struggled on how do we say a thing that's in a registry is of one or the other. Is it a Helm chart? Is it a singularity? Is it a wasm or is it a container image? Or, you know, where are you going to see now? So I agree with the data model here. I think it's a right approach actually having the config and the references separated. Like, I mean, what Sam's proposing would certainly be compatible with what we have today. I think more compatible with what we have today. I think the data model you're proposing here is better. My opposition is specifically that we consider that like an OCI index. I think we should call it something else. And I think we should just call it like a manifest index or manifest artifact, manifest or something, and completely separate it from OCI images. That's my proposal in terms of like where this should go, but I think the data model is right from a notary perspective. The thing I'm trying to separate or tease apart is what Sam's suggesting is mutually exclusive. We can do both. I think that's one of the separate problems. Because where we, if we want to store something in a manifest list that allows us to get direct access to it, that solves a bunch of performance things. And that's super cool.  I'm trying to tease out of the two problems. And you could actually argue that if I know that this index is a notary index as opposed to a multi arc index, then it actually frees up my ability to make the manifest list be a descriptor list actually. And it's actually pointing to the signature directly as opposed to a manifest that points to a blob or config. I'm not saying that very clearly. There's too many things in that conversation. The memory phrase. How do we, how do we know that this thing, this collection, the manifest list is not as a signature list as opposed to a multi arc list. That's it for Sam to answer. I mean, I think the question is, like, why do you want to, what's the, what do you need in order to make that distinction. Like, you can look at the media types of the things that are in the list of manifests and say, Oh, this one's a signature, and then I know that that's signature. True, like we've, in fact, Linux does, you know, is the non extension model in Linux that you parse the data and you can figure out the ideas, is there a more explicit thing that you can just say, without having to parse a collection and do some inference. Is there a reason not to have a way to directly say this index is of type is a signature, it is not a multi arc index as opposed to a registry having to parse the collection and make some inference around it. So if I do a repo listing in a registry, I want to be able to clearly see which, what type each thing is in the registry, whether it be a digest or a tag. Is this a container image and is it Windows or Linux? Is it a helm chart? Is it a singularity? Is it a wasm? Is it a signature? That's the simplicity of what we're trying to do. I mean, those are all things that you figure out from an index today. Like if you look at an index today, you're going to say, Oh, well, I, you know, this one's a Linux arm 64 image. This one's a Windows AMD 64 image. This one's a wasm image that's, that's contained in the descriptor in the index right now. But it's a multi arc index. The index, the index doesn't care, at least in the spec, whether it's a multi arc index or anything else. It's just a collection of things. Right. But today, the use case is it's a collection of container images that are differentiated by platform. And that is what most indexes are today, but that's not like the spec doesn't restrict it to that. Well, true, the restructors got a lot of respect has got a lot of openness and a lot of places, which is what we're trying to take advantage of, but we're also trying to figure out how do we not break, you know, what's the minimal impact we can have to something. So today, the proposal we've been having around the config object in index set lets a container be client, for instance, when it does a poll, that it would see that this is not a multi arc index that the media type, there either is no config object, or if there is a config object that it doesn't explicitly say it's a multi arc that it says, not for me, and just bails. And that's, that's kind of the model that we were focusing on the compatibility. If we say that this thing is still a multi basically a multi arc index but there's some other things in it. How does it know how to parse the different collections and in fact in a registry. When I list things, what you are saying that when I look at the, the index that I can see that the, you know, inside that index there's only an excellent windows and an arm and so forth. The question is the tag itself that says it's a multi arc index. That's what we're trying to differentiate. In fact, I think we've got this working in these here and I can show the example. Yes, I think one thing that I find kind of troubling about the idea of adding a field and calling it the same thing is how are registries supposed to handle that. When you push an index out that has a config field that config fields just ignored. And then somebody goes to pull that like oh there's a config I'm going to pull it the registry, you go to pull it and the registry like I don't have this object, because it didn't know anything was referencing it, because it had no idea that there was a config field. It like pushes the problem of registry compatibility down to the polling client which is the worst place to try to handle it. I was trying to multitask and didn't hear everything you were saying. Yeah, just saying that like you can you can add it, you can add support for it in the registry. But is if you're trying to use the same types, the same OCI type, this config field, the older registries aren't going to know aren't going to look at that field. And they're just, it's just going to be a lost object that a client's going to come down poll, say, oh, oh it has a config object therefore I'm going to go down this other branch of logic, and it's going to fail because the registry didn't keep that keeps that type or keeps that object because it didn't know that that object is being referenced. Yeah. So, like, what we let's just go back to how we did it with manifests right because this is basically trying to apply the same pattern we did on manifest to index. So, on a manifest, because this thing has this media type, we know it's a helm chart, and we can just pop the helm logo up here, as opposed to think I've got a singularity one in here as well. On this tag, it's a type singularity. Yeah, and I, I, I completely agree with this approach, like the I agree with the config field, like it's value in the manifest type. I think we're specifically here talking about the config field in the index type, because I agree that like having a config, and then some reference blobs is what we want. And we're stuck with these two, these two manifest types that are neither we have, we have in the have the regular manifest type which is clearly designed around images it has this weird word called layers in it. And then we have an index which has no config. So we're stuck with these two types, and we keep going back and forth about, like, Oh, should we put it in this type or if you put it in this type or should we. That's, that's all that's my recent proposal says, let's not use either those two types let's just introduce a third type. Either we use, we use the existing manifest type and just say layers means doesn't mean what you think it means. Or we use a new type, because if you take index and you add config, it is a new type. It has to be a new type it has to be a new version, and it has to be explicitly supported by clients and registries. Okay, so I just want to, I want to try and chip away in a couple of these separately because there's lots of different options we can go and just trying to make sure we're capturing the various problems. The reason I'm trying to show these two is just that this is how we differentiate the two types right so I'm just trying to see where we agree that we like this. Okay. If we like the way to differentiate the different types of manifest how a manifest can be used for different types. The thought process was we wanted to take that same concept on index that an index can either be a multi arc index, or it could be other things like a CNAP or a signature. The reason we were trying to use index for those two things for CNAP, it is a collection of artifacts, an invocation image, a couple of runtime images, hopefully the actual other artifacts that I would use for deployment like Helm and otherwise will be external things so that I don't have to rebake the invocation image each time. The reason that we were wanting to use it for index, sorry, signatures was we needed a way for something to reference something else. And if we remember in the discovery options, yeah, here we go. So in the example, let me make sure I got the right one. Yeah. So here's the example where we were exploring storing a signature as a manifest. If we store a signature as a manifest, how does one manifest declare it references something else. Now, we could change the manifest schema to add another property to it. What we, working with what we had, what we did, what we proposed was in the config object, this is the actual No, here this, the config object would store what it was referencing and registry could say, Oh, in the config object, I see what you're referencing and let me store that linkage. It felt weird that we are introducing that kind of behavior that now every registry has to pack crack the config of this particular type. This, this type to know what it's pointing at when an index already knows how to refer refer other things. So the question I'm kind of hearing here is, should a registries that today know how to deal with two different schemas manifest an index manifest being a single thing and it's being a collection of things. Should we introduce a third type. Yeah, my argument is if you add a config field you are introducing a third type, no matter what, like if you if you change the index, you can't just invisibly handle it like it is a third You can't invisibly right but that's what we wanted to use the versioning that says this property is an optional property in this new version of the scheme of this evolution is not a breaking change it's now to change. We wanted to test downward clients to see how they handle it. Yeah, that was my point last time is that there's no such thing as like no breaking change, like we learned this with OCI, we went from Docker to OCI. Yes, we, you can, you can version it, you can increase the version but it is a new type that needs to be handled like there's no, there's no magic that that's going to handle that they happen to. Until Tina runtime clients know how to handle signatures, they, the premise is we would version the schema and they should be forward compact aware, if that's the right word. They should say, look, I don't support anything greater than this version so I'm not going to accept anything until I know how to deal with it. So therefore, can I try and see if I'm understanding your concerns specifically is that you're worried that a registry which performs garbage collection is going that doesn't know about the config field is going to delete the object pointed to by the config field because there's no reference to it. Yep, that's one of the exact. Yep. So, I think Steve what, what Derek is saying is that if there is such a registry this isn't a forward compatible change, because the registry will lose track of the object. It's, it's potentially a forwards compatible change on the client side, but not on the registry side. Because we got multiple adding the new config field. And a new digest in there. If the registry doesn't know about it. It's going to lose track of the object that's referenced by that descriptor. Oh, the config object in other words, yes. Yes, so we think so there's two sets of compatibilities right there's the registries and there's six or eight of them major registries or whatever you know less than a dozen let's just say. And while there's probably less than a dozen container clients, there's how many deployments of those. So, part of this is trying to wrestle where what change and what the impact has. And the thought process is that registries today either don't support index, or if they do, many of them are checking the media type. And if the media type isn't of type index, they basically toss it anyway. So, if we add something, being able to communicate with the various registries on the registry side is less impactful than trying to find every compute node in the world that is running a container client container host. Yeah, but then you're still stuck in the situation where now you're trying to add a config, but you're also trying to be forward compatible or backward compatible with multi arc images. So essentially, every use of the index in this case is just trying to accommodate the possibility that a client may interpret it as a multi arc image, rather than let the clients explicitly say, I don't know what this is. And if it is a multi arc image, that's fine. Just leave it as a multi arc image but the idea that you're just trying to like, you're trying to reuse something in a way which you think that an older client will misinterpret it in the right way is kind of difficult. Like, there's already approach where the clients will explicitly tell the registry, these are the media types that I support. Right. So like we already have a way to do that. It's not always pretty, but we already have a way to do that. We do like the idea of having another type that's at the root because today, it's just index and manifest. The thing that we've been trying to do is figure out how do we not have a huge impact on that because what we're basically saying is for the most how it. Yeah, we can make it should registries have to know about these new schemas. That's what I'm trying to avoid. This is in parallel to because I don't want to derail the notary discussion, but maybe we should start up another thread on the manifest side to introduce something because yeah, in reality we would need to introduce this first in the manifest before we could use it in the notary or sorry in the artifacts back. So if we introduce something there, then, then maybe we can consider it here but yeah I agree like we don't, we can parallelize this we don't need to continue down that way. We do that we're going to wind up having this discussion the notary call that in fact on the notary call last couple weeks we've not had a bunch of notary specific conversations because we're trying to get them blocked on this so it's okay. Let's get a quick question around that so it sounds like we're worried about backward compatibility for registry implementations that just that only focus on those two media types and that anything else they might GC out of the way or do something untoward with wouldn't be in violation of the spec. I mean this the spec says that minimally those two media types are to be supported and that other media types may be in play and if you know I mean it's you know these specs are sort of RFC light I think that they're not hard coded specs but I mean, given that that is in the spec, it feels like it would be in violation to say well it's not these two that I care about so I'm just going to nuke those media type. I think that's why that's what Sam's proposals about or what Sam was suggesting because yeah the registry should not throw away should not do that anything that's in that manifest in that manifest array. However, anything that's in that config field a manifest may throw away because that was not part of the specification. You can't just add something to the specification later on and like you have to you have to create a new version right and when you create a new version. It is a new it is a new spec like like I know you're saying like oh it's just a minor version number. It's not like it's it's different behavior. It's not the same thing. So, right we were kind of at rat on. So, um, the, the, okay, do we, this one's not sure it's on this Wednesday's OCI agenda. So folks were talking to come back but I haven't seen anything yet. This is the thing that's kind of wrapping us up to try to throw up the next steps because we definitely want to get this resolved. I'm trying to get the right form to have this conversation because we're pulling it all the things that we've kind of gotten here and a bunch of these are great conversations to reevaluate. That's that's all point of the prototype is go forward and go back like no turned out we didn't like that let's go back and change it. But we do need to figure out what's the right thing to move forward with even if we might do it. That sounds good. Maybe I'll talk to you more. I think we talked a little bit after meeting last time. Steve, but yeah, maybe we should have a try to come up with a try to come up with a real proposal that can take to OCI proposing. Yeah, this change, or what the right way to get this changes I mean I agree with you with with the config like in the data model like it. I think we're just naming and where everything goes. Anyway, it's after prototyping. And the whole challenge is do how much special case and we want to do for signatures versus anything else. I'm like, the WASM thing came out of left field but that's the whole point of it. Because like they didn't have to do anything special. And that's what we're trying to enable and not make, make sure that as new types come up registries don't need to do anything special. Maybe what I'll do is list a couple of the things that were kind of in this and then we can decide, you know, that's not important or this one's big enough that we should make a change or maybe we'll make this one change and now all these new things get lit up. So, if that's kind of my hope with the artifact spec that that we can we can propose a solution there from the artifacts angle and if if the right solution is is versioning or adding a new version of the image index and we can but yeah I think we should propose this from the artifacts angle, not just the notary angle. Let me see what the agenda winds up being for Wednesday and if the solo folks aren't coming back then let's put this on Wednesday's conversation. Are you available. I see Sam's gone. Justin that's kind of late for you to. I can try to make sure I'm available, but I'm, I mean, I can, I can make it anytime though. Yeah, I can. I don't mind reaching out to you and Sam and well pretty much everybody's on this call.  Cormac you're muted. Sorry, put something on slack anyway. If you do a dark or something and put it on slack as well. Thanks folks. Cheers. Thanks.


Welcome to the 6th Philippine American International Book Festival. Happening this October 15th and 16th at the San Francisco Main Library at 100 Larkin Street.


Yeah, yeah, yeah, I did that already. I saw the last guy came up here, tried to use the Wi-Fi, so I was like, oh, I will preload every page. I'm already there. Alright, so it is officially 215. Hi everybody, I'm Adam Englender, and I'm here to talk to you, believe it or not, about the infinite wonder of the Symphony Event Dispatcher. And it may sound like a flowery title, but if you're a Drupal or getting used to the Symphony stuff, I got used to the Symphony 2 stuff when it first came out, and it was really hard and really difficult, but once I got it, the potential was endless on what you could do with it. So the first thing I want to try and figure out from everybody is why you're here. So how many are Drupal developers trying to learn about the Symphony nonsense? Alright, Symphony developer trying to learn about Drupal? Okay, okay. PHP developer trying to learn about Drupal, okay, and you just thought it was a cool presentation title. Alright, alright, alright, okay. Anybody here for none of the above? Alright, alright, honest man, I appreciate that. So this should be interesting for you, .NET. C sharp? Okay, so it's not so terrible. So how do we get here? So how I got here is that I started PHP with PHP 3, didn't do a whole lot in it because you couldn't do a whole lot in it, but then found Drupal. I've been building web pages and websites for myself, for people, and I'm a terrible designer, and I was terrible at HTML, and I got sick and tired of constantly changing content for people, and I found someone hooked me up with Drupal. I was like, this is amazing, this is the most amazing thing I've ever seen.  I can put in a database, put in the website, not have to write a whole lot of code and customize it a little bit. So the way that you would customize Drupal in the long, long ago is you would copy core or template code to your site code, hack around it for what you needed, beam with the pleasure of customizing Drupal, break things unknowingly, and only patch them required because you changed core code. This is the before time. How many people remember those times? Quite a few of you. Then came Drupal 5 through 7, right? And you still kind of copy core to customize true core functionality, but then hooks came about, right? And hooks are kind of cool so you can do things without having to go modify core code and you can make modules and all this kind of fun stuff. Use global functions to access everything. And upgrade was fairly simple as long as the hooks or global functions didn't really change. So if they didn't change how a hook worked, you were still cool, but if they did, then you had problems, right? How many people are in this boat? Probably most of you, right? Drupal 7? Yeah, most of you. And unit tests? You can kind of do it. Not too many boos, but I actually also write WordPress plugins, because I have to. And it's all the same thing, it's global functions and you have to write, there's a way you can write unit tests and it's a terrible way to go about it, but I did it, I'm not proud of it, but it works. So Drupal 8 was symphony. How many people have used Drupal 8 in an actual project? Okay, okay, that's not bad. How many people would love to, but it's too hard to figure out at this point? So it's not a whole lot different, but it uses event subscribers for hooks, which is what this whole talk is about, right? It uses dependency injection for core services, so you're not using those global functions anymore. It uses portable code in an open ecosystem, right? It allows you through all the composer stuff, the dependency injection talk from the last guy, Ashwini, is that you can use code from somewhere else. You don't have to write everything yourself. One of the hardest problems that I had with PHP before the revolution started with composer is that you had to write everything yourself because people were not writing good portable code, because they didn't need to, because nobody else used it, right? You would use pair and it was terrible and it was horrible. But that's changed, right?  And the other big thing is that it is fully unit testable, fully unit testable, right? If you use the dependency injection stuff from the last talk, you'll be able to understand how easy it is to test. How many people write unit tests? All right, all right, all right. And those of you who don't is probably because it's hard and doesn't make a whole lot of sense, because it's really hard if you're module developers, any module developers? Okay. So in writing your modules, right, it's super hard to find out when stuff breaks, because everything is a global function, and how do you mock a global function? Tell it it's a bad person, but that doesn't help. So when you're actually using dependency injection, and you're using all this stuff, it allows for full unit testable. And one of the nice things about this as you can also upgrade it will, because you know things aren't going to break, right? When you're hooking into with the dependency injection container, you're just kind of grabbing stuff that's in there and using it. And since people is using interfaces now, they can't really change that. They can't change how that works just kind of willy nilly, because it's a function that can do whatever they want. Right now, it's an interface, and there's a contract there that says this is what it is, and this is what it does. And this is how you call it. So you'll know very quickly when stuff doesn't work, because you'll try to access a function that doesn't exist, not why isn't it calling my callback? Why is my hook not running? Why is my hook broken? That's a big difference. So why do you want to use an event dispatcher? Is anyone here familiar with the concept of a design pattern called PubSub? Okay. So when you want to use an event subscriber, you want to use it because it creates highly modular code. Build you modules. Build modules and templates like building blocks. Once again, reusable code and open ecosystem and proven libraries inside the Drupal ecosystem. And so what you're doing here is you're actually listening for events. So the old world was a hook, right? You get a hook, it does something, you do a couple things and it goes out. The one thing that you really didn't do, and I mean, I haven't seen a whole lot of it, is nobody really hooks into the hook system, right? From your own module to call your own hook. How does someone knows they can do that, right? How does someone know where to do that to hook into your stuff? And modular code creates highly testable code at the unit level, right? I think that's kind of just to repeat what we talked about. It's going to allow you to do decoupled code and highly decoupled code is part of an event manager, right? In the Drupal stuff, you're injecting services in the connection container and it doesn't require you to know knowledge of the core systems. You don't have to instantiate an object, right? You don't have to use the new keyword, right? To build something and know how to build it. You don't have to.  So I need to call it. And it allows for a mix and match of components providing services based on events. And that's the big thing of events. So if there's something that you wanted to jam into Drupal core, how would you know where to do it? The example that I'm going to show you in a bit actually stops it before it processes anything. And I don't have to know where that's going on in Drupal core. I don't have to know what piece I have to copy out and change and replace inside of Drupal core to do this. What I can do is I can listen for an event. All I need to know is that event and what I can do with the structure of that event. And so that's a huge power there in that I can register an event. I'm going to get an object back similar to a hook. But the difference when the hook is, is that I'm going to actually get an object that I can deal with that's very, very specific about what it can do. I'm going to be able to make changes or pull data out. So one of the big things is a lot of the events in there, the same thing with the hooks with the note events and things like that. You're going to be able to modify a note as it comes through. That's a little powerful. But the other thing that you can do that I do inside of my modules is that you can now trigger events. So I can put that event handler inside of my module and I can trigger an event that I did something. And so if you care about it, you can do something about that too. So it allows you to have all these different modules that can talk to each other through kind of a single standard interface without having to know anything about each other or whether they're installed or not, is you're basically just saying, hey, I'm module A. I did this. You might care. You might not. But I'm going to tell the world that I did it so they can do something about it if they want to. Which is allowing you to act like Drupal core, right? You can go and just make your things extensible. So if you have a very popular module, people don't have to contribute all of your module to add functionality.  It allows you to build modules like building blocks. So you can say, I don't care about that functionality. I don't want all that extra junk. I don't want the database hits. I don't want the external calls. All I really want is the core piece. But I might want this other thing. So you can install one module and install a module based on that module that's going to listen for events. And basically, you're creating your own hook system, but you're using the embedded internal system to do that, which is pretty sweet. And that's how you do, like, the mixing and matching of components. And it allows for highly extensible code. So, like I was just talking about, you can take something very core, a very simple module that is listening to very specific events and doing something and saying, hey, I've done this. Do you care if you do do something about it? And so you can just build on top of each other without having to basically extend somebody else's module. Because the way you do it now is you kind of extend their module or just copy their stuff into your stuff or do whatever you do to add somebody's functionality to your code. And with the way that you do it with events and event-driven development is you don't have to do that. If someone writes their module well, they can just trigger an event. And hopefully, you're always triggering events. The two things that you can do super easy doesn't cost you anything, but gives you a lot is to log everything and trigger an event for everything. One of the things I do constantly is I'm doing, I have debug logs, right? You can inject the logger super easy and just debug log debug log debug log debug log. So I don't have to write print statements when something's going wrong. I can go look at the Drupal logs and say, hey, what happened? Which I actually had to do for my test thing because I couldn't figure out what's wrong because I had a bug in my code that I missed in the tests. Logic can be extracted to services. So in a hook, everything exists in that hook. And it's a function and it has access to what it has in the function, right? So it's this big kind of junk chunk that has to do everything by itself. When you're using the event manager, you're getting an event that's being called and it's calling a service that's registered inside the injection container. So I've got all the things that I need that are inside of there and I've got it already pre-built and it's pre-cached and it's super fast and I can do whatever I need to do as well as using things that other people wrote, right? I can inject the logger. I didn't have to write a logger. I don't have to know if it's going to work if I do error log or if I do a print statement what's going to happen. I don't have to figure that stuff out. I can inject all that stuff in. I know it works and I can test I'm actually doing the logging that I should be doing. And your logic can be extracted outside the Drupal environment. If you write something super cool for Drupal, nobody else can use it, right? Because it kind of works inside of Drupal. That's really all that it does. What you can do now is you can go, oh, people really like this functionality. The Drupal world really loves it and I'm making some money off of this or some notoriety. Why don't I take it and I port it to WordPress or I port it to Symphony or I port it to Laravel or cake or whatever you might want to do some other framework that might use it. Magento, right? Anything that's using the Symphony Core, you can throw cake out because it doesn't do that. But you can use that same event model or you can package up your code and split it out so that you're using it inside of your things that are doing the hooks, right? Always have to check time. Services can register and trigger their own investor extensibility, right? That's what we're talking about. You've got this service, it's listening to hooks, it's doing things, it's got these events, it's interacting, it's doing whatever it does, interacting with the core, but it's also allowing other modules to interact with it or other custom code to interact with it so that you might want to send an email every time this thing happens or pop up something on an admin or stick it in a log or do whatever you feel like, create a custom node or do something crazy. So up to this point, are there any questions before we get into the crazy stuff? Yeah. Yeah, so you can create, so the question is, can you create custom events, can you trigger your own events? Absolutely. So in the event dispatcher, all you do is you give it an event name, you say trigger event, and you give it a name, and that needs to be unique, otherwise people are listening to the wrong stuff, but usually you prepend it with your, your module name, dot, something, so you know what type of event it is. A lot of times you'll see using class constants for that, so that other people can know what it is without having to look at your docs. And then you just pass something that implements the event. So if you need to pass along another object, you extend event with another object, and it has accessors for these other pieces. And I have a, there's a demonstration that'll show you how you do that to some extent, at least to access the other pieces. That's your question? Yeah, so, and the whole thing is that you can do whatever you want, right? All you have to do is call the event dispatcher with a name that's unique, and an object that extends event. That's it. Any other questions? Yeah. Yes, it's nearly identical. Yeah, it's PubSub. It's standard PubSub. What you'll find if you're a Java developer, you'll find that symphony is a lot like Java. So the symphony dependency injection container is very similar to spring, spring dependency injection. And so, like, I was a Java developer for a while before I came into symphony, and I was, it made, I was like, oh, this makes so much sense. I totally get this. PHP developers were like, what? It is very different. It is very different, right? It's using some of the, one of the things I really like about symphony is that they took a lot of things from a lot of languages that aren't PHP and said, this is really popular. Let's port it to PHP, right? And the dependency injection container was probably the first one I'd ever seen in PHP. Before that, everything was using static methods, right? Zend used their static methods for their repository pattern and all this kind of crazy stuff. This is literally kind of building it all, and it even does a compiler pass where it actually takes all of these things, which is, by the way, when you do this development, always remember to run update.php or whatever you're using to basically reload your whole system because it compiles it all down to one giant file. Your dependency injection container becomes one giant file that has all of your classes in it and all that stuff, so it just loads one page, it gets cached one time, so that's what makes it super fast, but it also means that you have to reload it when you make changes. I discovered that when I was developing. I know how to do it in symphony, I didn't know how to do it inside of Drupal, and update PHP does that. It also does it whenever you install a module, it reloads all that, which kind of makes sense, because your modules are using the container and it's got to rebuild everything. So how to use the event dispatcher? So here's an oversimplified overview on how to use the event dispatcher. You create a service in your module that implements the event subscriber interface. All the Drupal docs have this, it's nothing super surprising, but the event subscriber interface basically is just one method that gets subscribed events, and you return an array. You write handlers for those events in your service, so the way that event subscribers work is that it's going to be the object that you're going to use needs to have the methods, and you just tell it what method it is. And then to get subscribed events method, you return a list of handled events. So it's an array, key value, the value can just be a string, or the value can be an array if you want to basically tell it what order you want it to be in, because you can specify priority for events that, oh, I need to fire before this other thing, so give me a higher priority. And then register the service with a tag event subscriber in your services YAML for your module. That's it, that's all it takes. And you are listening to events that you've registered for and it's going to call a function, just like registering for the hooks, right? Same process, although it's not in code, right? Some of it's in code, but it's not just I'm going to, on the load, you know, when I'm bootstrapping my module, I'm telling it, here's the events I care about, call these functions for these hooks, right? It's very similar, but it's a, it's a little cleaner and it's a lot easier to test. So in my opinion an example is worth a thousand slides. There's an example that I have up on GitHub, which I have here. So I created a Drupal module that basically what it does is it goes and gets an event. It registers for the getResponse event on the kernel. And if you don't know what the kernel is, that is the very core of symphony that says, oh, I got an HTTP request. I'm going to turn it into an object and then I'm going to call an event that says, get me a response to this request. And that's how all of symphony is event-based, right? Every piece of it is event-based from the kernel down. I can't say every piece of symphony because symphony is bigger than just the kernel. But inside of Drupal it's going to basically say you have the kernel that's going to say, hey, I need a response to this request. Get me a response, here's the request and it goes on. And it's basically a blacklist, right? So it says, is the IP in the white list? No? Tell them they can't, they're unauthorized. Right? I have no idea how you would do that in Drupal Core. I figured out how to do it using the event manager in like 10 minutes. And I know that there are a lot of security plugins, right?  You can actually just get the request before everything else and do what you want to do with it. And there's basically four files. Can everybody read that by the way? Yeah, yeah. Okay. And there's four files. So I have a blacklist service and this is my separate service that performs all of the functionality. I have an event subscriber which is the piece that is going to tell Drupal slash Symphony, I want to read this event. It's going to get the request and then it's going to call the service and see if it's one of the pieces that are there. And it's separating the functionality in that the event subscriber is handling all the Drupal type stuff, like get me the Drupal or Symphony type stuff, get me the request, get me the IP address and then it calls a service and says hey, is this IP address blocked? Right? So it's subtracting all that functionality that's not specific to deterring what the blacklist is to another service. Instead of having to do that all in one giant function or other global functions inside of a hook. And so it also has the services YAML file, which if you're doing Drupal 8, you'll module building, you'll get very familiar with it. It's a little overwhelming at first having to write all this nonsense. But once you understand it, it's a little easier and if you use the Symphony documentation, it's there's a lot of documentation, right? Ryan, who may or may not be here but he's speaking next, he wrote most of the documentation for Symphony and it's fantastic. There's a lot of documentation for Symphony and it's really good. Examples on how to do this, examples on how to do that, it makes a lot of sense. And then there's the ever-popular info.yaml for the file, right? So we all know the info.yaml has basically nothing in it. It just says hi, this is my module. It's a custom package and it needs, you know, core 8 or better. And what are my configuration settings? This is the services file. This can be confusing. Can everybody see that? No? Figure? Better? Okay. Yeah, so basically for this talk I couldn't figure out how to get the configuration stuff to work inside the container. So I'm setting a parameter which is hard coding the addresses that are whitelisted. And then I'm setting up my services. So I have a blacklist service that is the thing that's determining if you're blacklisted and it gets the whitelist, right? So it's just saying, hey, build this, build this service. Here's the pieces that it needs which is the number of the IPs that are whitelisted. And then we have, I wanted to, like I said, I log everything so I can figure out what's going on instead of having to like inject print statements and code and nonsense like that. So I created a logger channel for the Drupal event dispatch, for example. And then I injected that logger channel into my event subscriber. So I've got the subscriber, I've got the service. The really super important part here is that. That is what tells the container this thing wants to subscribe to events. It's tags. And tags are just a way for you to find things that you care about that are identifying themselves to you for another service in the container. Basically it's what's called a compiler pass. And it goes through and it says, oh, I want to add a compiler pass because I care about tags like this and I want to do something special for them because we're awesome. And so that's really what's kind of happening there. Is it's got a tag looking for event dispatcher and says, oh great, I need to know what these service names are and I need to know what the functions are. I'm going to call this thing and figure out what events that I have to trigger. Any questions about that? No? Are we too confused yet? Yeah. So event subscriber, again, this right here, my object has to implement the event subscriber interface which has a function called get subscribed events. So it's going to go, oh, and it's a it's also it's a static class. It's a static method on the class. So actually let me just, is this it? Yes. So, can we see that? Make it a little bit better. Okay. So get subscribed events, right? This is the actual thing that I have to implement in order to implement the interface event subscriber. And so I'm returning kernel event request, right? So static class attributes are awesome when you're using event names because I don't have to remember what your event name is. I just know that I'm looking for something on kernel events and looking for the request. And if you change that, my code doesn't break, right? Because my code is still using whatever you're calling it behind that that static thing. And then so I just have a super awesome named on kernel quest event, right? And so on kernel quest event, one thing that you have to know is that you're going to whatever objects or other method that you register, it's going to get an event. It's guaranteed to get something that implements the event interface. Beyond that is dependent on the particular event that you're listening for. And if you're listening to multiple events, the same function, you have to, you can kind of figure it out once you get it. But I happen to know because I looked at the documentation that on kernel quest returns a getResponse event, right?  Is that there's two types of requests. There are master requests and there are sub requests, right? Which means and you're both good, both can get the same data but all I really care about is the master request, which is the incoming request and going out. You can do sub requests as it does sub routes and all this kind of nonsense in your routing scheme and all that kind of junk that I don't care about, right?  So basically it checks to see if it's a master request, logs that it's checking that or which it is that it's not master not processing, right? So if I'm not a master, just log it. The other thing I want to do that you probably would do the opposite most times with a security plugin, I'm checking to make sure it's not admin so that it works out really well for my demo. Because when you get an unauthorized you have to actually uninstall it manually and reload, upload to get it to be able to uninstall it and turn it off. So in this one it basically says, hey, the event, the get requests or getResponse event has a method only called getRequest which is the incoming request and getPathInfo tells me what the path is. And so if it's admin I'm not going to process it because the path is admin so I'm not going to process for the blacklist because it doesn't work well for my demo. Otherwise it's not an admin and it's the master request so I actually want to see if this is supposed to be blacklisted. So I log that that's what I'm doing. I get the client IP from the request. There's one of the things that is super awesome for old school object oriented programmers like myself. In Drupal 8 is that everything is objects. Drupal 7 and almost every PHP based content management system out there nowadays you're using either standard class for the most part or using some sort of multi dimensional array to get all of your data and you've always got to check to make sure stuff is there. So if you're not flipping off warnings everywhere that to me is just a little annoying because they also change what is up in that object from time to time. There's no way to know what's going on. In the Drupal 8 world as far as I've seen so far everything's an object and it's a defined object and it's going to have a defined interface. They may add stuff later but the stuff that you was supposed to be there is always there and even on the symphony side they have probably the best migration thing I've ever seen. I've actually, the company I work at we're actually implementing that as well is that you deprecate it and then up to the next major release you put in deprecation notices and once you fix the deprecation notices you can move to the next major release with no problems. So the symphony stuff is going to be the least of your problems with Drupal upgrades I promise you. It is super, super, super stable. So basically I get the client IP from the request, I call my blacklist service and I ask is this IP blacklisted? If it is then I log a notice not a debug because sometimes people want to know why can't I access your system and if you're not looking at, if you're looking if you're not reporting debug logs you don't know. And then I throw an access denied HTTP exception. Now the one thing that I'm not doing here which I should be doing here is I should have been triggering another event to make this extensible. Because I could have said that oh IP blacklisted event. And you could do something about that if you really cared too. But I did not have time to do that for this particular piece here. And then more debug logging. So in honesty besides all the debug logging and all this kind of nonsense I have one function with one line. In the beginning this was all like three lines of code to say if it's the master request and it's not the admin path and it's blacklisted return denied. It's not the income request. There's a bunch of other stuff in there. And then the last piece is the actual blacklist service which basically gets an array of whitelisted and just returns if it's in the whitelisted array. There's no functionality really in there either. So but the thing that this actually does do is it shows some really core concepts specific to symphony which is dependency injection. Right. Building a modular module. Right. You're not just building a modular Drupal you're building a modular module that you're going to in theory if you wanted to find some other really cool library out there that did blacklisting way more sophisticated than I have you could just pull that library in and you could use that in your module and you could inject it into your event subscriber and you could just use that. Right. Instead of having to write everything. Right. I worked at a company for six years writing big PHP apps and we wrote everything and it was tiring and exhausting to have to be amazing at everything. Right.  And you wrote everything. Right. And there are there are Drupal modules out there that do a lot of very cool things but they're missing one piece here or one piece there because they're trying to be everything and what's happened a lot in the general PHP community. How many people here are familiar with packages. OK. Before pack how many people were here before packages. OK. Before packages life. So. It's. If you wanted to get your code if you had a really cool library. The only way that I could use your library is I would have to go know who you were. I'd have to go find where you put your code download a zip file or a tar ball and stick it inside my code. Now if you made any fixes to that I would have to somehow magically know that and update the code in my code because you made a change in yours or you could use pair. And pair. I understand why they did it but basically you would submit your code to pair they would decide if it was good enough to be put in there. And most stuff wasn't. And then. I don't know which came first. I've never asked Jordy or Fabian which came first symphony or composer. But when symphony came out there's so many modules that it used. You needed something to install all that stuff.  And all of a sudden this thing called composer showed up and it was the most amazing thing ever because packages didn't exist yet but you could basically say oh this git repository I want this tagged version. It was amazing. It changed my life forever. And then packages came out and now I can publish my library there and you can use it and know what it requires. And it will resolve all that stuff magically for you so that you can start including libraries. And what ended up happening is now there are tens of thousands of good libraries out there to do stuff that I don't want to write. I work for a security company. We write security software. We don't write logs. We don't write HTTP parsers. We write super secure with a lot of encryption. SDKs to access our security API. I don't want to have to write the rest. And I don't have to. Because I use a ton of libraries. I use PHP seclib even though I know a lot about crypto. I'm not an expert at crypto but there are people that are mathematicians that write PHP seclibs that I don't have to. And there are people that write monologue so I don't have to. And there are people that write the symphony event dispatcher so I don't have to. And that's my soapbox. Sorry. But I think that one thing to take away about and I know that it's actually going to be talked about a lot by Ryan in the next talk is that Drupal 8 opens up the world for Drupal developers. You can include anything from anywhere of the tens, not even up to hundreds of thousands of packages, on packages. Into your code. It's a little kind of weird how you have to do it but you can still do it in your module. So you can build these super awesome modules that do this super cool stuff that is super secure and super safe and you didn't have to write it. You just had to put the pieces together so it could be done. And then you can make it extensible because you can use the event manager so that people can build on top of yours without having to commit to your project. Right?  That's not really what my module's for. And no, that's not cool. But what they can do now, if you trigger events, is that they can build their own module that listens to the events from your module and does the crazy whack-out stuff you don't want in your core stuff. Makes it super awesome. Any questions about the example? Yeah. So in my example, is there a way for me to set priority for the event? And no. It's not doing that right now. So where you would do that is right here. This would just be an array and it would have two attributes or two items in the array and it would be the name and you would give it the priority. It's integer. Yeah. Can you see how it's doing that? Yeah. And offhand, I don't remember if higher or lower is better because there's so many of these things that some of them higher is better, some of them lower is better. But the documentation is in the symphony event dispatcher and how to do all that crazy stuff. And there's a few other things that you can do. Like you can actually tell it to call multiple functions and you can give an array of an array of... Yeah. So it allows you to do some fairly complex stuff but that's basically what it does. And you had a question? Yeah, I did. So what you would do is you would inside of whatever service you wanted, you would inject the event dispatcher. All right. It's another service that's in the container and I believe it's actually event underscore dispatcher. And so you would go into your services YAML file and you would inject it there. And then in here, when you got done, you would just say... You would trigger you would event dispatcher, trigger event, give it a name, give it an event object. That's it. That's all there is to it. Because that's all that the core is doing underneath, right? That's all that the symphony kernel is doing. It's going, oh, I got a request in. I've parsed the request and turned into this request object. I'm going to tell the world I need a response. Because what I'm doing here is I could actually set a response, but it actually works a whole lot better. If I set the response, it actually just returns a blank page that just has a 400, 401 response. If I do the access denied exception, it actually does a nice Drupal page. And what that actually looks like in an example, make sure it's still working.  And the instructions are actually on the thing on how to do this that basically you just go into Extends. Sorry for the tapping noises. Drupal event dispatcher example, install. It's going to rebuild the container. It's all done. And so now, if I go to the site, I've got access tonight, right? I've completely circumvented everything except for the template layer. And I'm just saying, you cannot access this site. Because I'm actually, and you're saying, but wait a minute, you're on localhost. Why can't you access it? And the actual reason for that is, is because I'm not on localhost. My address is 172.1701. So if you use this locally, you'll probably find and try to extra for somewhere else to be there. But because I'm in, I'm using docker. Thank you. Because I'm using docker, it picks its own IP addresses and everything's all happy. And here's all the logs, right? So I'm a real big believer in logging for people that aren't you, that don't know how your code works, to find out what's going on and why things are terrible. Because sometimes stuff just doesn't work. There's the things you don't think of, the things you don't expect. When you build a module and it's used out there by a million people that speak 12 different languages, sometimes you didn't think of everything. So logging is always really good. And so any more questions about the example? And I'll have a thing at the end if you want to take a look at it. And so in the last little bit of the talk, I want to talk about how this is only the beginning, right? Drupal 8 is the beginning, right? I promise you, it's just the beginning. It is such a change from Drupal 7, right?  I don't understand it. I don't want to figure it out. I just want to make money using Drupal. And I totally get it.  You're going to write better modules. You're going to write them faster. You're going to be able to test them. You're going to be able to put them under continuous integration to make sure that if somebody else commits to your module, that they don't break your stuff because you can just write tests around it. And that was the other thing that I didn't show is that I actually have unit tests around every one of those things. Because I can, it's just an object, right? It's not some global method calling another global method using a static method on a global object. That is really hard to test. And if you love unit tests, like I love unit tests, you'd be excited. But like I said, it's going to help you build a better model, module, right? You're going to have modular modules. I know that does make a lot of sense, but it allows you to just build on top of what someone else has done, right?  Because that's old school dribble. Just take what they wrote, steal it, copy it, change a couple of things. And you've got your own module. But you don't get their bug fixes, right? When you do it that way, when they find something broken and they fix it, you don't get it because you copy their stuff. Loosely coupled, right? So you can build modules that aren't dependent, that can work with other modules. But all they know about is that you, there's an event dispatcher and an event. They have no idea how your module works and they don't need to, right? You don't have to know how anything else works to build on top of it. And that's an amazing bit of power, right? I don't have to, I didn't have to know anything about symphony or Drupal to intercept the request and give an unauthorized response. I just had to know that the first thing it does is it fires off a getResponse event and that event has a request and I can throw an exception and it's done, right? I didn't have to learn anything about Drupal for that. I didn't have to learn anything about symphony for that. I just had to know about the events, right? That's really powerful. Highly extensible because it's loosely coupled. Highly configurable as you're building things for the events. You can configure it based on events. You can trigger it. You can basically have things, oh, I've got this event, this event, this event. So now I know as I'm trying to process my piece, I know the state that's going on from the other events. Now I can react in a way that I care about. And you can make it bulletproof, right? When you can test everything. When you can have reliable tests using mocked objects inside of your modules, you can make them bulletproof, right? You can find all the bugs. You can find all the edge cases, right? You can write really good code that people will look at and go, wow, that's pretty cool. I like what you did there. That's rare in a CMS, right? That someone's going to look at your code from something you built for a CMS and went, that's a really good instance of object-oriented code. That's pretty cool stuff. You can do that now, right? Drupal 8 allows that. And external libraries, right? Not having to write everything lets you concentrate on what you're doing, right? And you can build a better Drupal. So my hope is that there are a limited number of events that are out there, right? There's stuff from the symphony world that's already been sent and there's a few things, but for a security company, I'm trying to write a Drupal module. I can't, there's no event that says someone has submitted a login request, right? I have to go write my own login form and process that and replace the existing login form, right? So I still have to go do the stuff that I've always done in Drupal, which is copy it from core, change the piece I need, register it for my module and replace the existing one that's used by Drupal. But not too many boos, but on WordPress, it's actually triggers, right? I get a callback, which is a hook that says someone's trying to log in. If you return me a user object, then I'll assume they're logged in. Hopefully Drupal gets there, right? To get away from the copy, core and modify and be able to utilize more non-Drupal stuff, right? Inside of Drupal. There's probably a lot more code in there that is doing your own thing, which you're not, you know, depending on how important that is to that specific, that specific maintainer, you may get new stuff, you may not. And accelerate platform growth. When you don't have to write everything and you don't have to have intimate knowledge of the system to extend it, you can build things very quickly. And when they're under test, you can have a lot of people working on it and know it's not going to break. So this is my contact information. If you have any further questions, I think we actually have three minutes for questions or 13 minutes, okay. Just want to put that up there. Twitter, email, please rate my talk. There'll be another one up here. And then the example is already up there. The slides are at the speaker rate address. So if you want to get it there, which is there. I really, really appreciate any input people give me on these talks. I think I'm halfway decent at them. Some people say I suck, some people say I'm great. I would like to know what you think and what you'd like to see different. This is my first talk to Drupal folks. So, I'd like to know how you guys feel about it. Any questions? Question? Anyway, so you were talking about, okay, like if you want to have a login event, right? That's an event that's handled by core out of the box. What is your philosophy about, okay, well, if I wanted to do that the right way, maybe I'll do a form alter, right? I can hook in using the existing hooks, right? And create a place for me to shove in an event listener, right, I can fire off an event and then do it that way. What's your philosophy on sort of creating those event listeners for core? Right, so the event listeners are there for form actions, right? But I don't have a form action. What I want to do is I want to hook in to the login system, right? I want to hook into the off system, not the form. I don't want to change your form. I just want to add my service as a secondary factor to login to protect Drupal sites from hackers with multi-factor authentication, right? So what I have to do is I have to actually replace that form handler entirely so that I can do the login. So basically it's like I have to replace the form because that's what generates the form and listens to the form so that I can call the existing functionality and then do that. What I would prefer to have is you have prioritized events that is pre-off, post-off. Like in the best world, you have a pre and post of everything, right? So that I can pre this if I want to change the data that you're working with, I will affect it on a pre event. And if I want to know what you did, I can deal with that on a post event. But being able to just kind of slide yourself in there on a chain is a whole lot simpler than having to inject yourself in the middle because what happens when somebody else wants to do something with the form as well, right? So now they're replacing, so I've got to get in there first or last depending on how it works because if they replace the form, then my stuff is gone, right? So there's no way, you can't have like two people interacting with an override on the same form because you have to replace the form in the form handler. So are you aware of an initiative to get rid of hooks and replace all of them for Drupal 9 with event, is that where this is going then? Well, Drupal 8, I don't know if the hooks are there, I know that all the hooks are available as events. I don't know if the hooks are actually there, are they? Some are still there? Right, right, right. Right, and I think a lot of it is because you take a look at how many Drupal modules are we gonna destroy, right? That people use at every single Drupal site.  Well, I know, but you guys are still here, right?  Like if you took a look at the difference between Symphony 1 and Symphony 2, they are not even similar, right? I'm grateful, right? Because I never used Symphony 1, but I gladly used Symphony 2. So sometimes you need this massive change, but I imagine some of the stuff just still needed to be there because there's just too much code to change, like I said, I don't know if there's a movement or not, like I'm not super into the Drupal community, that's just my hope, right? Is that you start exposing more of this so that you can extend the platform more, you can extend it quicker, you've got a better opportunity to do so. So what you do is you extend the event object or you implement the event interface with another object and you put in centers or getters for that data. You inject it in the constructor, then you have getters to go get the things that you need, and it's passed with that event. So in the actual, in here, see I take the event, I'm getting the request type, right? So the event itself has whether or not this is a, it's very specific data to that event, right? All the event actually has, the only methods that it implements is propagation stopped and stop propagation, right? There's some other methods that are deprecated, but that's the only thing left in event, right? That's the only thing that's part of the actual event itself. Everything else has been added on afterwards. And that's how you put it in there, is you extend it with your own object that allows you to get the data out. Correct, you're gonna pass an event object, when it gets triggered, you're gonna trigger it and pass your event object that's gonna implement the event interface and then give those people who are listening to that event access to the data they need to react appropriately. Usually it's done with getters, not so much properties, like global or public properties. Yeah, it depends. So when you trigger your event, you may have pass it an object that you already have or reference to that it's gonna change. So like I could set the response to that event and then it would actually use that response that I gave it in the event handler. So it's gonna trigger the event and then get the data back from the event and it uses that response. Actually that's not correct. It's gonna be the, I'm trying to remember how the kernel request works. There's one at the very bottom that gets the response out and if it doesn't have anything, it builds its own unhandled response, right? Because nothing took care of this so I'm just gonna tell you nothing took care of it. So there's an event at the very bottom that gets all the data passed along in the event and says, oh, now I need to do something with it. But you also, if it passed in a response, you could get the response object, modify it and it would have that object if it passed it in. Question. I'm sorry, can you code the event from what? Can you code it, I mean, can you trigger it?  I mean, it's not something where, so if you want to trigger it manually, you would have something listening to the request that says, oh, this URL wants me to trigger something and you could do that in theory. Like I'm listening for things that aren't admin that are the master request and trigger and dealing with that. So you could do that if you wanted to, but it's not, you couldn't really do it from the command line unless you access the Drupal app, right? I mean, in theory, there are command line stuff where you could hook into Drupal and do that, but it's not so much where you could just magically make it trigger from the command line. Any other questions? All right, thank you for your time.