Design ====== Many products are subject to design choices in order to give the impression of consistent outer interface and inner behavior. Likewise, the development of NineKt itself has also been subject to design choices. This document intends to report the most important design choices that made NineKt what it is today Premises -------- These choices are a starting point and give a general shape to NineKt, while also choosing consistent routes for many design decision junctions. Most of these decisions were made before beginning the development. 1. NineKt must implement the 9P protocol in the most compliant way (which, ideally, is *completely* compliant) with what is described in [section 5 of 9front's manual pages](https://man.9front.org/5/). 2. Since 9P itself does not specify protocols for authentication or cryptography, NineKt's implementation of 9P must include the default authentication ("p9any") and cryptographic (TLS) protocols employed by 9front. (See `/rc/bin/service/tcp17020` in the source tree and the `listen(8)`, `tlssrv(8)`, `authsrv(6)` manual pages.) 3. NineKt can implement some variations in the 9P protocol, as long as they are widely used and sensible. Design choices -------------- 1. Kotlin's main programming paradigm is object-oriented. Therefore, NineKt must implement 9P using an object-oriented design. - The request types (T-messages) should be written in code as methods of an interface, which is then implemented by a class that manages the connection. Calls to these methods should reflect, and happen as a consequence of, the file system operations performed on the local synthetic file tree, once the 9P initialization process (version and message size negotiation, authentication, etc.) is complete. - The message types (for both T-messages and R-messages) should be defined as an enumeration class which also holds their respective values in 9P messages. - Messages ready to be sent should be encapsulated into the instantiation of a special class, because most messages *can* be abstracted into a general data structure. Such class holds the independent pieces of each message: the message type, a collection of the message fields (actually, their names) in the same order they are expected to be sent, the value for each field in a map, and the maximum message size, as negotiated by the initialization procedure. - The transport layer methods, which send and receive raw bytes of data, as well as other methods taking care of the security of the communication and the authentication of both parts, should follow the same realization in code as the T-message methods: an interface provides signatures, while classes provide a specific implementation for each of them. The case of security is special, as it does not need any additional method other than those defined by the transport layer's interface. In fact, the idea of reading from the connection and writing to it is already enough and initialization, whether it is of the transport layer protocol or the security layer on top of it, is performed in the implementor class' constructor, which additional arguments for specifying possible variations in the security implementations' behavior. 2. For the 9P initialization procedure, which hardly mutates between usages, at least in the way it is performed rather than in the data being exchanged, there should be a function that acts as a *macro* for the whole procedure, which calls the appropriate methods synchronously. Asynchronicity --------------