summaryrefslogtreecommitdiff
path: root/doc/design.md
blob: ee072aab9ead84471d6c7d7061d100f85a02a58c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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
--------------