BlocktreeCloudPaper.tex 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795
  1. \documentclass{article}
  2. \usepackage[scale=0.8]{geometry}
  3. \usepackage{hyperref}
  4. \usepackage{graphicx}
  5. \title{The Blocktree Cloud Orchestration Platform}
  6. \author{Matthew Carr}
  7. \begin{document}
  8. \maketitle
  9. \begin{abstract}
  10. This document is a proposal for a novel cloud platform called Blocktree.
  11. The system is described in terms of the actor model,
  12. where tasks and services are implemented as actors.
  13. The platform is responsible for orchestrating these actors on a set of native operating system processes.
  14. A service is provdied to actors which allows them access to a highly available distributed file system,
  15. which serves as the only source of persistent state for the system.
  16. High availability is achieved using the Raft consensus protocol to synchronize the state of files between processes.
  17. All data stored in the filesystem is secured with strong integrity and optional confidentiality protections.
  18. A network block device like interface allows for fast low-level read and write access to the encrypted data,
  19. with full support for client-side encryption.
  20. Well-known cryptographic primitives and constructions are employed to provide this protection,
  21. the system does not attempt to innovate in terms of cryptography.
  22. The system's trust model allows for mutual TLS authentication between all processes in the system,
  23. even those which are controlled by different owners.
  24. By integrating these ideas into a single platform,
  25. the system aims to advance the status quo in the security and reliability of software systems.
  26. \end{abstract}
  27. \section{Introduction}
  28. % The "Big" Picture.
  29. Blocktree is an attempt to extend the Unix philosophy that everything is a file
  30. to the entire distributed system that comprises modern IT infrastructure.
  31. The system is organized around a global distributed filesystem which defines security
  32. principals, resources, and their authorization attributes.
  33. This filesystem provides a language for access control that can be used to securely grant principals
  34. access to resources from different organizations, without the need to setup federation.
  35. The system provides an actor runtime for orchestrating services.
  36. Resources are represented by actors, and actors are grouped into operating system processes.
  37. Each process has its own credentials which authenticate it as a unique security principal,
  38. and which specify the filesystem path where the process is located.
  39. A process has authorization attributes which determine the set of processes that may communicate with it.
  40. Every connection between processes is established using mutual TLS authentication,
  41. which is accomplished without the need to trust any third-party certificate authorities.
  42. The cryptographic mechanisms which make this possible are described in detail in section 3.
  43. Messages addressed to actors in a different process are forwarded over these connections,
  44. while messages delivered to actors in the same process are delivered with zero-copying.
  45. % Self-certifying paths and the chain of trust.
  46. The single global Blocktree filesystem is partitioned into disjoint domains of authority.
  47. Each domain is controlled by a root principal.
  48. As is the case for all principals,
  49. a root principal is authenticated by a public-private key pair,
  50. and is identified by a hash of its public key.
  51. The domain of authority for a given absolute path is determined by its first component,
  52. which is the identifier of the root principal who controls the domain.
  53. Because there is no meaning to the directory "/",
  54. a directory consisting of only a single component equal to a root principal's identifier is
  55. referred to as the root directory of that root principal.
  56. The root principal delegates its authority to write files to subordinate principals by issuing
  57. them certificates which specify the path that the authority of the subordinate is limited to.
  58. File data is signed for authenticity and a certificate chain is contained in its metadata.
  59. This certificate chain must lead back to the root principal
  60. and consist of certificates with correctly scoped authority in order for the file to be authentic.
  61. Given the path of a file and the file's contents,
  62. this system allows the file to be validated by anyone without the need to trust a third-party.
  63. Blocktree paths are referred to as self-certifying for this reason.
  64. % Persistent state provided by the filesystem.
  65. One of the major challenges in distributed systems is managing persistent state.
  66. Blocktree solves this issue using its distributed filesystem.
  67. Files are broken into segments called sectors.
  68. The sector size of a file can be configured when it is created,
  69. but cannot be changed after the fact.
  70. Reads and writes of individual sectors are guaranteed to be atomic.
  71. The sectors which comprise a file and its metadata are replicated by a set of processes running
  72. the sector service.
  73. This service is responsible for storing the sectors of files which are contained in the directory
  74. containing the process in which it is running.
  75. The actors providing the sector service in a given directory coordinate with one another using
  76. the Raft protocol to synchronize the state of the sectors they store.
  77. This method of partitioning the data in the filesystem based on directory
  78. allows the system to scale beyond the capabilities of a single consensus cluster.
  79. Sectors are secured with strong integrity protection,
  80. which allows anyone to verify that their contents were written by an authorized principal.
  81. Encryption can be optionally applied to sectors,
  82. with the system handling key management.
  83. The cryptographic mechanisms used to implement these protections are described in section 3.
  84. To reduce load on the sector service, and to allow the system to scale to a larger number of users,
  85. a peer-to-peer distribution system is implemented in the filesystem service.
  86. This system allows filesystem actors to download sectors from other filesystem actors
  87. that have the sectors in their local cache.
  88. The threat of malicious actors serving bad sector data is mitigated by the strong integrity
  89. protections applied to sectors.
  90. By using peer-to-peer distribution, the system can serve as a content delivery network.
  91. % Protocol contracts.
  92. One of the design goals of Blocktree is to facilitate the creation of composable distributed
  93. systems.
  94. A major challenge to building such systems is the difficulty in pinning down bugs when they
  95. inevitably occur.
  96. Research into session types (a.k.a. Behavioral Types) promises to bring the safety benefits
  97. of type checking to actor communication.
  98. Blocktree integrates a session typing system that allows protocol contracts to be defined that
  99. specify the communication patterns of a set of actors.
  100. This model allows the state space of the set of actors participating in a computation to be defined,
  101. and the state transitions which occur to be specified based on the types of received messages.
  102. These contracts are used to verify protocol adherence statically and dynamically.
  103. This system is implemented using compile time code generation,
  104. making it a zero-cost abstraction.
  105. This frees the developer from dealing with the numerous failure modes that can occur in a
  106. communication protocol.
  107. % Implementation language and project links.
  108. Blocktree is implemented in the Rust programming language.
  109. Its source code is licensed under the Affero GNU Public License Version 3.
  110. It can be downloaded at the project homepage at \url{https://blocktree.systems}.
  111. Anyone interested in contributing to development is welcome to submit a pull request
  112. to \url{https://gogs.delease.com/Delease/Blocktree}.
  113. If you have larger changes or architectural suggestions,
  114. please submit an issue for discussion prior to spending time implementing your idea.
  115. % Outline of the rest of the paper.
  116. The remainder of this paper is structured as follows:
  117. \begin{itemize}
  118. \item Section 2 describes the actor runtime, service and task orchestration, and service
  119. discovery.
  120. \item Section 3 discusses the filesystem, its concurrency semantics and implementation.
  121. \item Section 4 details the cryptographic mechanisms used to secure communication between
  122. actor runtimes and to protect sector data.
  123. \item Section 5 is a set of examples describing ways that Blocktree can be used to build systems.
  124. \item Section 6 provides some concluding remarks.
  125. \end{itemize}
  126. \section{Actor Runtime}
  127. % Motivation for using the actor model.
  128. Building scalable fault tolerant systems requires us to distribute computation over
  129. multiple computers.
  130. Rather than switching to a different programming model when an application scales beyond the
  131. capacity of a single computer,
  132. it is beneficial in terms of programmer time and program simplicity to begin with a model that
  133. enables multi-computer scalability.
  134. Fundamentally, all communication over an IP network involves the exchange of messages,
  135. namely IP packets.
  136. So if we wish to build scalable fault-tolerant systems,
  137. it makes sense to choose a programming model built on message passing,
  138. as this will ensure low impedance with the underlying networking technology.
  139. % Overview of message passing interface.
  140. That is why Blocktree is built on the actor model
  141. and why its actor runtime is at the core of its architecture.
  142. The runtime can be used to spawn actors, register services, and dispatch messages.
  143. Messages can be dispatched in two different ways: with \texttt{send} and \texttt{call}.
  144. A message is dispatched with the \texttt{send} method when no reply is required,
  145. and with \texttt{call} when exactly one is.
  146. The \texttt{Future} returned by \texttt{call} can be awaited to obtain the reply.
  147. If a timeout occurs while waiting for the reply,
  148. the \texttt{Future} completes with an error.
  149. The name \texttt{call} was chosen to bring to mind a remote procedure call,
  150. which is the primary use case this method was intended for.
  151. Awaiting replies to messages serves as a simple way to synchronize a distributed computation.
  152. % Description of virtual actor system.
  153. One of the challenges when building actor systems is supervising and managing actor's lifecycles.
  154. This is handled in Erlang through the use of supervision trees,
  155. but Blocktree takes a different approach inspired by Microsoft's Orleans framework.
  156. Orleans introduced the concept of virtual actors,
  157. which are purely logical entities that exist perpetually.
  158. In Orleans, one does not need to spawn actors nor worry about respawing them should they crash,
  159. the framework takes care of spawning an actor when a message is dispatched to it.
  160. This model also gives the framework the flexibility to deactivate actors when they are idle
  161. and to load balance actors across different computers.
  162. In Blocktree a similar system is used when messages are dispatched to services.
  163. The Blocktree runtime takes care of routing these messages to the appropriate actors,
  164. spawning them if needed.
  165. % Message addressing modes.
  166. Messages can be addressed to services or specific actors.
  167. When addressing a specific actor,
  168. the message contains an \emph{actor name},
  169. which is a pair consisting of the path of the runtime hosting the actor and the \texttt{Uuid}
  170. identifying the specific actor in that runtime.
  171. When addressing a service,
  172. the message is dispatched using a \emph{service name},
  173. which contains the following fields:
  174. \begin{enumerate}
  175. \item \texttt{service}: The path identifying the receiving service.
  176. \item \texttt{scope}: A filesystem path used to specify the intended recipient.
  177. \item \texttt{rootwards}: An boolean describing whether message delivery is attempted towards or
  178. away from the root of the filesystem tree. A value of
  179. \texttt{false} indicates that the message is intended for a runtime directly contained in the
  180. scope. A value of \texttt{true} indicates that the message is intended for a runtime contained
  181. in a parent directory of the scope and should be delivered to a runtime which has the requested
  182. service registered and is closest to the scope.
  183. \item \texttt{id}: An identifier for a specific service provider.
  184. \end{enumerate}
  185. The ID can be a \texttt{Uuid} or a \texttt{String}.
  186. It is treated as an opaque identifier by the runtime,
  187. but a service is free to associate additional meaning to it.
  188. Every message has a header containing the name of the sender and receiver.
  189. The receiver name can be an actor or service name,
  190. but the receiver name is always an actor name.
  191. For example, to open a file in the filesystem,
  192. a message is dispatched with \texttt{call} using the service name of the filesystem service.
  193. The reply contains the name of the file actor spawned by the filesystem service which owns the opened
  194. file.
  195. Messages are then dispatched to the file actor using its actor name to read and write to the file.
  196. % The runtime is implemented using tokio.
  197. The actor runtime is currently implemented using the Rust asynchronous runtime tokio.
  198. Actors are spawned as tasks in the tokio runtime,
  199. and multi-producer single consumer channels are used for message delivery.
  200. Because actors are just tasks,
  201. they can do anything a task can do,
  202. including awaiting other \texttt{Future}s.
  203. Because of this, there is no need for the actor runtime to support short-lived worker tasks,
  204. as any such use-case can be accomplished by awaiting a set of \texttt{Future}s.
  205. This allows the runtime to focus on providing support for services.
  206. Using tokio also means that we have access to a high performance multi-threaded runtime with
  207. evented IO.
  208. This asynchronous programming model ensures that resources are efficiently utilized,
  209. and is ideal for a system focused on orchestrating services which may be used by many clients.
  210. % Delivering messages over the network.
  211. Messages can be forwarded between actor runtimes using a secure transport layer called
  212. \texttt{bttp}.
  213. The transport is implemented using the QUIC protocol, which integrates TLS for security.
  214. A \texttt{bttp} client may connect anonymously or using credentials.
  215. If an anonymous connection is attempted,
  216. the client has no authorization attributes associated with it.
  217. Only runtimes which grant others the execute permission allow connections from such clients.
  218. If these permissions are not granted in the runtime's file,
  219. anonymous connections are rejected.
  220. When a client connects with credentials,
  221. mutual TLS authentication is performed as part of the connection handshake,
  222. which cryptographically verifies the credentials of each runtime.
  223. These credentials contain the filesystem paths where each runtime is located,
  224. which ensures that messages addressed to a specific path will only be delivered to that path.
  225. The \texttt{bttp} server is always authenticated during the handshake,
  226. even when the client is connecting anonymously.
  227. Because QUIC supports the concurrent use of many different streams,
  228. it serves as an ideal transport for a message oriented system.
  229. \texttt{bttp} uses different streams for independent messages,
  230. ensuring that head of line blocking does not occur.
  231. Note that although data from separate streams can arrive in any order,
  232. the protocol does provide reliable in-order delivery of data in a given stream.
  233. The same stream is used for sending the reply to a message dispatched with \texttt{call}.
  234. Once a connection is established,
  235. message may flow both directions (provided both runtimes have execute permissions for the other),
  236. regardless of which runtime is acting as the client or the server.
  237. % Delivering messages locally.
  238. When a message is sent between actors in the same runtime it is delivered into the queue of the recipient without any copying,
  239. while ensuring immutability (i.e. move semantics).
  240. This is possible thanks to the Rust ownership system,
  241. because the message sender gives ownership to the runtime when it dispatches the message,
  242. and the runtime gives ownership to the recipient when it delivers the message.
  243. % Security model based on filesystem permissions.
  244. A runtime is represented in the filesystem as a file.
  245. This file contains the authorization attributes which are associated with the runtime's security
  246. principal.
  247. The credentials used by the runtime specify the file, so other runtimes are able to locate it.
  248. The metadata of the file contains authorization attributes just like any other file
  249. (e.g. UID, GID, and mode bits).
  250. In order for a principal to be able to send a message to an actor in the runtime,
  251. it must have execute permissions for this file.
  252. Thus communication between runtimes can be controlled using simple filesystem permissions.
  253. Permissions checking is done during the \texttt{bttp} handshake.
  254. Note that it is possible for messages to be sent in one direction in a \texttt{bttp} connection
  255. but not in the other.
  256. In this situation replies are permitted but unsolicited messages are not.
  257. An important trade-off which was made when designing this model was that messages which are
  258. sent between actors in the same runtime are not subject to any authorization checks.
  259. This was done for two reasons: performance and security.
  260. By eliminating authorization checks messages can be more efficiently delivered between actors in the
  261. same process,
  262. which helps to reduce the performance penalty of the actor runtime over directly using threads.
  263. Security is enhanced by this decision because it forces the user to separate actors with different
  264. security requirements into different operating system processes,
  265. which ensures all of the process isolation machinery in the operating system will be used to
  266. isolate them.
  267. % Representing resources as actors.
  268. As in other actor systems, it is convenient to represent resources in Blocktree using actors.
  269. This allows the same security model used to control communication between actors to be used for
  270. controlling access to resources,
  271. and for resources to be shared by many actors.
  272. For instance, a Point-to-Point Protocol connection could be owned by an actor.
  273. This actor could forward traffic delivered to it in messages over this connection.
  274. The set of actors which are able to access the connection is controlled by setting the filesystem
  275. permissions on the file for the runtime executing the actor owning the connection.
  276. % Actor ownership.
  277. The concept of ownership in programming languages is very useful for ensuring that resources are
  278. properly freed when the type using them dies.
  279. Because actors are used for encapsulating resources in Blocktree,
  280. a similar system of ownership is employed for this reason.
  281. An actor is initially owned by the actor that spawned it.
  282. An actor can only have a single owner,
  283. but the owner can grant ownership to another actor.
  284. An actor is not allowed to own itself,
  285. though it may be owned by the runtime.
  286. When the owner of an actor returns,
  287. the actor is sent a message instructing it to return.
  288. If it does not return after a timeout,
  289. it is interrupted.
  290. This is the opposite of how supervision trees work in Erlang.
  291. Instead of the parent receiving a message when the child returns,
  292. the child receives a message when the parent returns.
  293. Service providers spawned by the runtime are owned by it.
  294. They continue running until the runtime chooses to reclaim their resources,
  295. which can happen because they are idle or the runtime is overloaded.
  296. % Message routing to services.
  297. A service is identified by a Blocktree path.
  298. Only one service implementation can be registered in a particular runtime,
  299. though this implementation may be used to spawn many actors as providers for the service,
  300. each associated with a different ID.
  301. The runtime spawns a new actor when it finds no service provider associated with the ID in the
  302. message it is delivering.
  303. Some services may only have one service provider in a given runtime,
  304. as is the case for the sector and filesystem services.
  305. Services are reactive,
  306. they don't do anything until they receive a message to process.
  307. The \texttt{scope} and \texttt{rootward} field in an actor name specify the set of runtimes to
  308. which a message may be delivered.
  309. They allow the sender to express their intended recipient,
  310. while still affording enough flexibility to the runtime to route messages as needed.
  311. If \texttt{rootward} is \texttt{false},
  312. the message is delivered to a service provider in a runtime that is directly contained in
  313. \texttt{scope}.
  314. If \texttt{rootward} is \texttt{true},
  315. the parent directories of scope are searched,
  316. working towards the root of the filesystem tree,
  317. and the message is delivered to the first provider of \texttt{service} which is found.
  318. When there are multiple service providers to which a given message could be delivered,
  319. the one to which it is actually delivered is unspecified,
  320. which allows the runtime to balance load.
  321. Delivery will occur for at most one recipient,
  322. even in the case that there are multiple potential recipients.
  323. In order to contact other runtimes and deliver messages to them,
  324. their IP addresses need to be known.
  325. This is achieved by maintaining a file with a runtime's IP address in the same directory as the
  326. runtime.
  327. The runtime is granted write permissions on the file,
  328. and it is updated by \texttt{bttp} when it begins listening on a new endpoint.
  329. The services which are allowed to be registered in a given runtime are specified in the runtime's
  330. file.
  331. The runtime reads this list and uses it to deny service registrations for unauthorized services.
  332. The list is also read by other runtime's when they are searching a directory for service providers.
  333. % The sector and filesystem service.
  334. The filesystem is itself implemented as a service.
  335. A filesystem service provider can be passed messages to delete files, list directory contents,
  336. open files, or perform several other standard filesystem operations.
  337. When a file is opened,
  338. a new actor is spawned which owns the newly created file handle and its name is returned to the
  339. caller in a reply.
  340. Subsequent read and write messages are sent to this actor.
  341. The filesystem service does not persist any data itself,
  342. its job is to function as an integration layer,
  343. conglomerating sector data from many different sources into a single unified interface.
  344. The sector service is what is ultimately responsible for storing data,
  345. and thus maintaining the persistent state of the system.
  346. It stores sector data in the local filesystem of each computer on which it is registered.
  347. The details of how this is accomplished are deferred to the next section.
  348. % Runtime network discovery.
  349. While it is possible to resolve runtime paths to IP addresses when the filesystem is available,
  350. a different mechanism is needed to allow the filesystem and sector services to discover service
  351. providers.
  352. To facilitate this,
  353. runtimes are able to query one another to learn about other runtimes.
  354. Because queries are intended to facilitate message delivery,
  355. the query fields and their meanings mirror those used for addressing messages:
  356. \begin{enumerate}
  357. \item \texttt{service} The path of the service whose providers are sought.
  358. Only runtimes with this service registered will be returned.
  359. \item \texttt{scope} The filesystem path relative to which the query will be processed.
  360. \item \texttt{rootward} Indicates if the query should search for runtimes from \texttt{scope}
  361. toward the root.
  362. \end{enumerate}
  363. The semantics of \texttt{scope} and \texttt{rootward} in a query are identical to their use in an
  364. actor name.
  365. As long as at least one other runtime is known,
  366. a query can be issued to learn of more runtimes.
  367. A runtime which receives a query may not be able to answer it directly.
  368. If it cannot,
  369. it returns the IP address of the next runtime to which the query should be sent.
  370. In order to bootstrap the discovery processes,
  371. another mechanism is needed to find the first peer to query.
  372. There were several possibilities explored for doing this.
  373. One way is to use a blockchain to store the IP addresses of the runtimes hosting the sector service
  374. in the root directory.
  375. As long as these runtimes could be located,
  376. then all others could be found using the filesystem.
  377. This idea may be worth revisiting in the future,
  378. but the author wanted to avoid the complexity of implementing a new proof of work blockchain.
  379. Another idea was to use multicast link-local addressing to discover other runtimes,
  380. similar to how mDNS operates.
  381. This approach has several advantages.
  382. It avoids any dependency on centralized internet infrastructure
  383. and keeps network load local to the segment on which the runtimes are connected.
  384. But, it will not work over a wide area network,
  385. making it unsuitable for the general case.
  386. Instead, the design which was decided on was to use DNS to resolve a fully qualified domain name
  387. (FQDN) derived from the root principal's identifier.
  388. This FQDN is expected to resolve to the public IP addresses of the runtimes hosting the
  389. sector service in the root directory of the root principal.
  390. Each process is configured with a search domain which is used as a suffix of the FQDN.
  391. The leading labels in the FQDN are computed by base32 encoding a hash of the root
  392. principal's public key.
  393. If the encoded string is longer than 63 bytes (the limit for each label in a hostname),
  394. it is separated into the fewest number of labels possible,
  395. working from left to right along the string.
  396. A dot followed by the search domain is concatenated onto the end of this string to form the FQDN.
  397. This method has the advantages of being simple to implement
  398. and allowing runtimes to discover each other over the internet.
  399. Implementing this system would be facilitated by hosting DNS servers in actors in the same
  400. runtimes as the root sector service providers.
  401. Then, A or AAAA records could be served which point to these runtimes.
  402. These runtimes would also need to be configured with static IP addresses,
  403. and the NS records for the search domain would need to point to them.
  404. Of course it is also possible to build such a system without hosting DNS inside of Blocktree.
  405. The downside of using DNS is that it couples Blocktree with a centralized,
  406. albeit distributed, system.
  407. % Security model for queries.
  408. To allow runtimes which are not permitted to execute the root directory to query for other runtimes,
  409. authorization logic which is specific to queries is needed.
  410. If a process is connected with credentials
  411. and the path in the credentials contains the scope of the query,
  412. the query is permitted.
  413. If a process is connected anonymously,
  414. its query will only be answered if the query scope
  415. and all of its parent directories,
  416. grant others the execute permission.
  417. Queries from authenticated processes can be authorized using only the information in the query,
  418. but anonymous queries require knowledge of filesystem permissions,
  419. some of which may not be known to the answering runtime.
  420. When authorizing an anonymous query,
  421. an answering runtime should check that that the execute permission is granted on all directories
  422. that it is responsible for storing.
  423. If all these checks pass, it should forward the querier to the next runtime as usual.
  424. % Overview of protocol contracts and runtime checking of protocol adherence.
  425. To facilitate the creation of composable systems,
  426. a protocol contract checking system based on session types has been designed.
  427. This system models a communication protocol as a directed graph representing state transitions
  428. based on types of received messages.
  429. The protocol author defines the states that the actors participating in the protocol can be in using
  430. Rust traits.
  431. These traits define handler methods for each message type the actor is expected to handle in that
  432. state.
  433. A top-level trait which represents the entire protocol is defined that contains the types of the
  434. initial state of every actor in the protocol.
  435. A macro is used to generate the message handling loop for the each of the parties to the protocol,
  436. as well as enums to represent all possible states that the parties can be in and the messages that
  437. they exchange.
  438. The generated code is responsible for ensuring that errors are generated when a message of an
  439. unexpected type is received,
  440. eliminating the need for ad-hoc error handling code to be written by application developers.
  441. % Example of a protocol contract.
  442. % TODO: I don't find this example very compelling. It would be more impressive to show a pub-sub
  443. % protocol, that would look cool.
  444. Let us explore the use of this system through a simple example using the HTTP/1.1 protocol.
  445. It is a state-less client-server protocol,
  446. essentially just an RPC from client to server.
  447. We can model this in for the contract checker by defining a trait representing the protocol:
  448. \begin{verbatim}
  449. pub trait Http {
  450. type Server: ServerInit;
  451. }
  452. \end{verbatim}
  453. The purpose of this top-level trait is to specify the initial state of every party to the
  454. communications protocol.
  455. In this case we're only modeling the state of the server,
  456. as the client will just \texttt{call} a method on the server.
  457. The initial state for the server is defined as follows:
  458. \begin{verbatim}
  459. pub trait ServerInit {
  460. type AfterActivate: Listening;
  461. type Fut: Future<Output = Result<Self::AfterActivate>>;
  462. fn handle_activate(self, msg: Activate) -> Self::Fut;
  463. }
  464. \end{verbatim}
  465. \texttt{Activate} is a message sent by the generated code to allow the actor access to the
  466. runtime and the actor's ID.
  467. It is defined as follows:
  468. \begin{verbatim}
  469. pub struct Activate {
  470. rt: &'static Runtime,
  471. act_id: Uuid,
  472. }
  473. \end{verbatim}
  474. We represent the statelessness of HTTP by having the requests to the \texttt{Listening} state
  475. return another \texttt{Listening} state.
  476. \begin{verbatim}
  477. pub trait Listening {
  478. type AfterRequest: Listening;
  479. type Fut: Future<Output = Result<Self::AfterRequest>>;
  480. fn handle_request(self, msg: Envelope<Request>) -> Self::Fut;
  481. }
  482. \end{verbatim}
  483. The \texttt{Envelope} type is a wrapper around a message which contains information about who sent
  484. it and a method which can be used to send a reply.
  485. In general a new type could be returned after each message received,
  486. with the returned type being dependent on the type of the message.
  487. The state graph of this protocol can be visualized as follows:
  488. \begin{center}
  489. \includegraphics[height=1.5in]{HttpStateGraph.pdf}
  490. \end{center}
  491. % Implementing actors in languages other than Rust.
  492. Today the actor runtime only supports executing actors implemented in Rust.
  493. A WebAssembly (Wasm) plugin system is planned to allow any language which can compile to Wasm to be
  494. used to implement an actor.
  495. This work is blocked pending the standardization of the WebAssembly Component Model,
  496. which promises to provide an interface definition language which will allow type safe actors to be
  497. defined in many different languages.
  498. % Running containers using actors.
  499. Blocktree allows containers to be run by encapsulating them using a supervising actor.
  500. This actor is responsible for starting the container and managing the container's kernel namespace.
  501. Logically, it owns any kernel resources created by the container, including all spawned operating
  502. system processes.
  503. When the actor halts,
  504. all of these resources are destroyed.
  505. All network communication to the container is controlled by the supervising actor.
  506. The supervisor can be configured to bind container ports to host ports,
  507. as is commonly done today,
  508. but it can also be used to encapsulate traffic to and from the container in Blocktree messages.
  509. These messages are routed to other actors based on the configuration of the supervisor.
  510. This essentially creates a VPN for containers,
  511. ensuring that regardless of well secured their communication is,
  512. they will be safe to communicate over any network.
  513. This network encapsulation system could be used in other actors as well,
  514. allowing a lightweight and secure VPN system to built.
  515. \section{Filesystem}
  516. % The division of responsibilities between the sector and filesystem services.
  517. The responsibility for serving data in the system is shared between the filesystem and sector
  518. services.
  519. Most actors will access the filesystem through the filesystem service,
  520. which provides a high-level interface that takes care of the cryptographic operations necessary to
  521. read and write files.
  522. The filesystem service relies on the sector service for actually persisting data.
  523. The individual sectors which make up a file are read from and written to the sector service,
  524. which stores them in the local filesystem of the computer on which it is running.
  525. A sector is the atomic unit of data storage
  526. and the sector service only supports reading and writing entire sectors at once.
  527. File actors spawned by the filesystem service buffer reads and writes until there is enough
  528. data to fill a sector.
  529. Because cryptographic operations are only performed on full sectors,
  530. the cost of providing these protections is amortized over the size of the sector.
  531. Thus there is tradeoff between latency and throughput when selecting the sector size of a file:
  532. a smaller sector size means less latency while a larger one enables more throughput.
  533. % Types of sectors: metadata, integrity, and data.
  534. A file has a single metadata sector, a Merkle sector, and zero or more data sectors.
  535. The sector size of a file can be specified when it is created,
  536. but cannot be changed later.
  537. Every data sector contains the ciphertext of the number of bytes equal to the sector size,
  538. but the metadata and Merkle sectors contain a variable amount of data.
  539. The metadata sector contains all of the filesystem metadata associated with the file.
  540. In addition to the usual metadata present in any Unix filesystem (the contents of the \texttt{stat} struct),
  541. cryptographic information necessary to verify and decrypt the contents of the file are also stored.
  542. The Merkle sector of a file contains a Merkle tree over the data sectors of a file.
  543. The hash function used by this tree can be configured at file creation,
  544. but cannot be changed after the fact.
  545. % How sectors are identified.
  546. When sector service providers are contained in the same directory they connect to each other to form
  547. a consensus cluster.
  548. This cluster is identified by a \texttt{u64} called the cluster's \emph{generation}.
  549. Every file is identified by a pair of \texttt{u64}, its generation and its inode.
  550. The sectors within a file are identified by an enum which specifies which type they are,
  551. and in the case of data sectors, their 0-based index.
  552. \begin{verbatim}
  553. pub enum SectorKind {
  554. Meta,
  555. Merkle,
  556. Data(u64),
  557. }
  558. \end{verbatim}
  559. The byte offset in the plaintext of the file at which each data sector begins can be calculated by
  560. multiplying the sector's index by the sector size of the file.
  561. The \texttt{SectorId} type is used to identify a sector.
  562. \begin{verbatim}
  563. pub enum SectorId {
  564. generation: u64,
  565. inode: u64,
  566. sector: SectorKind,
  567. }
  568. \end{verbatim}
  569. % Types of messages handled by the sector service.
  570. Communication with the sector service is done by passing it messages of type \texttt{SectorMsg}.
  571. \begin{verbatim}
  572. pub struct SectorMsg {
  573. id: SectorId,
  574. op: SectorOperation,
  575. }
  576. pub enum SectorOperation {
  577. Read,
  578. Write(WriteOperation),
  579. }
  580. pub enum WriteOperation {
  581. Meta(Box<FileMeta>),
  582. Data {
  583. meta: Box<FileMeta>,
  584. contents: Vec<u8>,
  585. }
  586. }
  587. \end{verbatim}
  588. Here \texttt{FileMeta} is the type used to store metadata for files.
  589. Note that updated metadata is required to be sent when a sector's contents are modified.
  590. % Scaling horizontally: using Raft to create consensus cluster. Additional replication methods.
  591. A generation of sector service providers uses the Raft protocol to synchronize the state of the
  592. sectors it stores.
  593. The message passing interface of the runtime enables this implementation
  594. and the sector service's requirements were important considerations in designing this interface.
  595. The system currently replicates all data to each of the service providers in the cluster.
  596. Additional replication methods are planned for future implementation
  597. (e.g. erasure encoding and distribution via consistent hashing),
  598. which allow for different tradeoffs between data durability and storage utilization.
  599. % Scaling vertically: how different generations are stitched together.
  600. The creation of a new generation of the sector service is accomplished with several steps.
  601. First, a new directory is created in which the generation will be located.
  602. Next, one or more processes are credentialed for this directory,
  603. using a procedure which is described in the next section.
  604. The credentialing process produces files for each of the processes stored in the new directory.
  605. The sector service provider in each of the processes uses the filesystem service
  606. (which connects to the parent generation of the sector service)
  607. to find the other runtimes hosting the sector service in the directory and messages them to
  608. establish a fully-connected cluster.
  609. Finally, the service provider which is elected leader contacts the generation in the root directory
  610. and requests a new generation number.
  611. Once this number is known it is stored in the superblock for the generation,
  612. which is the file identified by the new generation number and inode 2.
  613. The superblock is not contained in any directory and cannot be accessed outside the sector service.
  614. The superblock also keeps track of the next inode to assign to a new file.
  615. % Authorization logic of the sector service.
  616. To prevent malicious actors from writing invalid data,
  617. the sector service must cryptographically verify all write messages.
  618. The process it uses to do this involves several steps:
  619. \begin{enumerate}
  620. \item The certificate chain in the metadata that was sent in the write message is validated.
  621. It is considered valid if it ends with a certificate signed by the root principal
  622. and the paths in the certificates are correctly nested,
  623. indicating valid delegation of write authority at every step.
  624. \item Using the last public key in the certificate chain,
  625. the signature in the metadata is validated.
  626. This signature covers all of the fields in the metadata.
  627. \item The new sector contents in the write message are hashed using the digest function configured
  628. for the file and the resulting hash is used to update the file's Merkle tree in its Merkle
  629. sector.
  630. \item The root of the Merkle tree is compared with the integrity value in the file's metadata.
  631. The write message is considered valid if and only if there is a match.
  632. \end{enumerate}
  633. This same logic is used by file actors to verify the data they read from the sector service.
  634. Only once a write message is validated is it shared with the sector service provider's peers in
  635. its generation.
  636. Although the data in a file is encrypted,
  637. it is still beneficial for security to prevent unauthorized principal's from gaining access to a
  638. file's ciphertext.
  639. To prevent this, a sector service provider checks a file's metadata to verify that the requesting
  640. principal actually has a readcap (to be defined in the next section) for the file.
  641. This ensures that only principals that are authorized to read a file can gain access to the file's
  642. ciphertext, metadata, and Merkle tree.
  643. % File actors are responsible for cryptographic operations. Client-side encryption.
  644. The sector service is relied upon by the filesystem service to read and write sectors.
  645. Filesystem service providers communicate with the sector service to open files and perform
  646. filesystem operations.
  647. These providers spawn file actors that are responsible for verifying and decrypting the information
  648. contained in sectors and providing it to other actors.
  649. They use the credentials of the runtime they are hosted in to decrypt sector data using
  650. information contained in file metadata.
  651. File actors are also responsible for encrypting and integrity protecting data written to files.
  652. In order for a file actor to produce a signature over the root of the file's Merkle tree,
  653. it maintains a copy of the tree in memory.
  654. This copy is read from the sector service when the file is opened.
  655. While this does mean duplicating data between the sector and filesystem services,
  656. this design was chosen to reduce the network traffic between the two services,
  657. as the entire Merkle tree does not need to be transmitted on every write.
  658. Encapsulating all cryptographic operations in the filesystem service and file actors allows the
  659. computer storing data to be different from the computer encrypting it.
  660. This approach allows client-side encryption to be done on more capable computers
  661. and low powered devices to delegate this task to a storage server.
  662. % Prevention of resource leaks through ownership.
  663. A major advantage of using file actors to access file data is that they can be accessed over the
  664. network from a different runtime as easily as they can be from the same runtime.
  665. One complication arising from this approach is that file actors must not outlive the actor which
  666. caused them to be spawned.
  667. This is handled in the filesystem service by making the actor who opened the file the owner of the
  668. file actor.
  669. When a file actor receives notification that its owner returned,
  670. it flushes any buffered data in its cache and returns,
  671. ensuring that a resource leak does not occur.
  672. % Authorization logic of the filesystem service.
  673. The filesystem service uses an \texttt{Authorizer} type to make authorization decisions.
  674. It passes this type the authorization attributes of the principal accessing the file, the
  675. attributes of the file, and the type of access (read, write, or execute).
  676. The \texttt{Authorizer} returns a boolean indicating if access is permitted or denied.
  677. These access control checks are performed for every message processed by the filesystem service,
  678. including opening a file.
  679. A file actor only responds to messages sent from its owner,
  680. which ensures that it can avoid the overhead of performing access control checks as these were
  681. carried out by the filesystem service when it was created.
  682. The file actor is configured when it is spawned to allow read only, write only, or read write
  683. access to a file,
  684. depending on what type of access was requested by the actor opening the file.
  685. % Streaming replication.
  686. Often when building distributed systems it is convenient to alert any interested party that an event
  687. has occurred.
  688. To facilitate this pattern,
  689. the sector service allows actors to subscribe for notification of writes to a file.
  690. The sector service maintains a list of actors which are currently subscribed
  691. and when it commits a write to its local storage,
  692. it sends each of them a notification message identifying the sector written
  693. (but not the written data).
  694. By using different files to represent different events,
  695. a simple notification system can be built.
  696. Because the contents of a directory may be distributed over many different generations,
  697. this system does not support the recursive monitoring of directories.
  698. Although this system lacks the power of \texttt{inotify} in the Linux kernel,
  699. it does provides some of its benefits without incurring much or a performance overhead
  700. or implementation complexity.
  701. For example, this system can be used to implement streaming replication.
  702. This is done by subscribing to writes on all the files that are to be replicated,
  703. then reading new sectors as soon as notifications are received.
  704. These sectors can then be written into replica files in a different directory.
  705. This ensures that the contents of the replicas will be updated in near real-time.
  706. \section{Cryptography}
  707. % The underlying trust model: self-certifying paths.
  708. % Verifying sector contents on read and certifying on write.
  709. % Confidentiality protecting files with readcaps. Single pubkey operation to read a dir tree.
  710. % Give example of how these mechanisms allow data to be shared without any prior federation.
  711. % Description of bttp handshake and the authentication data which is provided by both parties.
  712. % Requesting and issuing credentials. Multicast link-local network discovery.
  713. \section{Examples}
  714. This section contains examples of systems built using Blocktree. The hope is to illustrate how this
  715. platform can be used to implement existing applications more easily and to make it possible to
  716. implement systems which are currently out of reach.
  717. \subsection{A personal cloud for a home user.}
  718. % Describe my idealized home Blocktree setup.
  719. \subsection{An ecommerce website.}
  720. % Describe a blocktree which runs a cluster of webservers, a manufacturing process, a warehouse
  721. % inventory management system, and an order fulfillment system.
  722. \subsection{A smart home.}
  723. \subsection{A realtime geo-spacial environment.}
  724. % Explain my vision of the metaverse.
  725. \section{Conclusion}
  726. % Blocktree serves as the basis for building a cloud-level distributed operating system.
  727. % The system enables individuals to self-host the services they rely on.
  728. % It also gives business a freeer choice of whether to own or lease computing resources.
  729. % The system advances the status quo in secure computing.
  730. % Composability leads to emergent benefits.
  731. \end{document}