Переглянути джерело

Updated the website and the rustdocs.

Matthew Carr 1 рік тому
батько
коміт
28eaf91e10

+ 4 - 0
crates/btfproto/src/client.rs

@@ -1,4 +1,5 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
+//! This module contains the [FsClient] struct, which is used to access a filesystem server.
 use crate::{msg::*, server::FsProvider, Handle, Inode};
 
 use btlib::{bterr, crypto::ConcretePub, BlockPath, IssuedProcRec, Result};
@@ -119,11 +120,14 @@ where
     }
 }
 
+/// A struct which can be used to perform filesystem operations on a remote server.
 pub struct FsClient {
     tx: Transmitter,
 }
 
 impl FsClient {
+    /// Creates a new [FsClient] which sends all filesystem operations to the server using the
+    /// given [Transmitter].
     pub fn new(tx: Transmitter) -> Self {
         Self { tx }
     }

+ 4 - 0
crates/btfproto/src/lib.rs

@@ -1,7 +1,11 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
+//! This crate contains the definition of the protocol used to access the filesystem, as well as
+//! its client and server implementations.
 #![feature(impl_trait_in_assoc_type)]
 
+/// The type for inodes in used in the filesystem.
 pub type Inode = btlib::Inode;
+/// The type for open file handles used in the filesystem.
 pub type Handle = u64;
 
 pub mod msg;

+ 3 - 1
crates/btfproto/src/local_fs.rs

@@ -1,4 +1,5 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
+//! Contains the types used to implement [LocalFs].
 use crate::{msg::*, server::FsProvider};
 
 use btlib::{
@@ -46,6 +47,7 @@ mod private {
     type Inode = u64;
     type Handle = u64;
 
+    /// Represents the errors which can occur in the [LocalFs] type.
     #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
     pub enum Error {
         NotOpen(Inode),
@@ -587,7 +589,7 @@ mod private {
         inode_key: Vec<u8>,
     }
 
-    /// Structure for managing the part of a blocktree which is stored in the local filesystem.
+    /// A struct for managing the part of a blocktree stored in the local filesystem.
     pub struct LocalFs<A> {
         /// The path to the directory in the local filesystem where this blocktree is located.
         path: PathBuf,

+ 92 - 22
crates/btfproto/src/msg.rs

@@ -1,4 +1,5 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
+//! Definitions of messages passed between filesystem clients and servers.
 use super::{Handle, Inode};
 
 use btlib::{
@@ -14,6 +15,8 @@ use std::{
     ops::{BitOr, BitOrAssign},
 };
 
+/// Top-level message type for the filesystem protocol.
+/// All messages passed from clients to servers are of this type.
 #[derive(Serialize, Deserialize)]
 pub enum FsMsg<'a> {
     #[serde(borrow)]
@@ -41,8 +44,10 @@ pub enum FsMsg<'a> {
     GrantAccess(GrantAccess),
 }
 
+/// The type for every reply sent from servers to clients.
 #[derive(Serialize, Deserialize)]
 pub enum FsReply<'a> {
+    /// Indicates a message was received successfully but does not provide any additional data.
     Ack(()),
     Lookup(LookupReply),
     Create(CreateReply),
@@ -60,21 +65,6 @@ impl<'a> CallMsg<'a> for FsMsg<'a> {
     type Reply<'b> = FsReply<'b>;
 }
 
-#[derive(Debug, Serialize, Deserialize)]
-pub enum FsError {
-    Other,
-}
-
-impl Display for FsError {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            Self::Other => write!(f, "uncategorized error"),
-        }
-    }
-}
-
-impl std::error::Error for FsError {}
-
 #[repr(u64)]
 /// An enumeration of special Inodes.
 pub enum SpecInodes {
@@ -158,39 +148,64 @@ impl BitOr<libc::mode_t> for FileType {
 
 #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
 #[repr(i32)]
-/// The generators for the group of [Flags]. These are mostly copied from [libc], save for
-/// several custom values. Note that the presence of a flag in this enum does not guarantee
-/// it's supported.
+/// The generators for the group of [Flags].
+/// 
+/// These are mostly values from libc, save for several custom values. Note that the presence of a
+/// flag in this enum does not guarantee it's supported.
+/// The standard libc `O_*` value which corresponds to each variant is given in the variant's
+/// comment.
+/// Consult your libc documentation for an explanation of what these means.
 pub enum FlagValue {
     // Standard flags.
+    /// `O_RDONLY`
     ReadOnly = libc::O_RDONLY,
+    /// `O_WRONLY`
     WriteOnly = libc::O_WRONLY,
+    /// `O_RDWR`
     ReadWrite = libc::O_RDWR,
+    /// `O_ACCMODE`
     AccMode = libc::O_ACCMODE,
+    /// `O_CREAT`
     Create = libc::O_CREAT,
+    /// `O_EXCL`
     Exclusive = libc::O_EXCL,
+    /// `O_NOCTTY`
     NoCtty = libc::O_NOCTTY,
+    /// `O_TRUNC`
     Truncate = libc::O_TRUNC,
+    /// `O_APPEND`
     Append = libc::O_APPEND,
+    /// `O_NONBLOCK`
     NonBlock = libc::O_NONBLOCK,
+    /// `O_DSYNC`
     Dsync = libc::O_DSYNC,
+    /// `O_ASYNC`
     Async = libc::O_ASYNC,
+    /// `O_DIRECT`
     Direct = libc::O_DIRECT,
+    /// `O_DIRECTORY`
     Directory = libc::O_DIRECTORY,
+    /// `O_NOFOLLOW`
     NoFollow = libc::O_NOFOLLOW,
+    /// `O_NOATIME`
     NoAtime = libc::O_NOATIME,
+    /// `O_CLOEXEC`
     CloseExec = libc::O_CLOEXEC,
+    /// `O_RSYNC`
     Rsync = libc::O_RSYNC,
+    /// `O_PATH`
     Path = libc::O_PATH,
+    /// `O_TMPFILE`
     TmpFile = libc::O_TMPFILE,
     // Custom flags.
     /// Indicates that a process block should be created.
     Process = 0x01000000,
-    /// Indicates that a server block be created.
+    /// Indicates that a server block should be created.
     Server = 0x02000000,
 }
 
 impl FlagValue {
+    /// Returns the underlying [i32] value.
     pub const fn value(self) -> i32 {
         self as i32
     }
@@ -219,44 +234,55 @@ pub struct Flags(i32);
 impl Copy for Flags {}
 
 impl Flags {
+    /// Wraps the given [i32] value.
     pub const fn new(value: i32) -> Self {
         Self(value)
     }
 
+    /// Returns the inner [i32] value.
     pub const fn value(self) -> i32 {
         self.0
     }
 
-    /// Returns true if and only if the given open flags allow the file to be written to.
+    /// Returns true if these flags allow the file to be written to.
     pub const fn writeable(self) -> bool {
         const MASK: i32 = FlagValue::ReadWrite.value() | FlagValue::WriteOnly.value();
         self.0 & MASK != 0
     }
 
+    /// Returns true if these flags allow the file to be read from.
     pub const fn readable(self) -> bool {
         !self.write_only()
     }
 
+    /// Returns true if the file was opened readonly.
     pub const fn read_only(self) -> bool {
         self.0 == FlagValue::ReadOnly.value()
     }
 
+    /// Returns true if the file was opened writeonly
     pub const fn write_only(self) -> bool {
         self.0 & FlagValue::WriteOnly.value() != 0
     }
 
+    /// Returns true if these flags are for a directory.
     pub const fn directory(self) -> bool {
         self.0 & FlagValue::Directory.value() != 0
     }
 
+    /// Returns true if these flags are for a process.
     pub const fn process(self) -> bool {
         self.0 & FlagValue::Process.value() != 0
     }
 
+    /// Returns true if these flags are for a server.
     pub const fn server(self) -> bool {
         self.0 & FlagValue::Server.value() != 0
     }
 
+    /// Asserts that these flags allow a file to be read.
+    /// 
+    /// If the assertion fails then an [io::Error] with the errno [libc::EACCES] is returned.
     pub fn assert_readable(self) -> Result<(), io::Error> {
         if !self.readable() {
             Err(io::Error::from_raw_os_error(libc::EACCES))
@@ -265,6 +291,9 @@ impl Flags {
         }
     }
 
+    /// Asserts that these flags allow a file to be written.
+    /// 
+    /// If the assertion fails then an [io::Error] with the errno [libc::EACCES] is returned.
     pub fn assert_writeable(self) -> Result<(), io::Error> {
         if !self.writeable() {
             Err(io::Error::from_raw_os_error(libc::EACCES))
@@ -318,6 +347,7 @@ impl Default for Flags {
     }
 }
 
+/// Attributes of a file.
 #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Hash, Default)]
 pub struct Attrs {
     pub mode: u32,
@@ -330,8 +360,9 @@ pub struct Attrs {
 }
 
 #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Hash)]
-/// A type for indicating which fields in [Attrs] have been set and which should be ignored. This
-/// method was chosen over using [Option] for greater efficiency on the wire.
+/// A type for indicating which fields in [Attrs] have been set and which should be ignored.
+/// 
+/// This method was chosen over using [Option] for greater efficiency on the wire.
 pub struct AttrsSet(u16);
 
 macro_rules! field {
@@ -399,6 +430,10 @@ impl BitOrAssign<Self> for AttrsSet {
     }
 }
 
+/// A filesystem entry.
+/// 
+/// This struct includes attributes of a file as well as cache control
+/// information for how long it may be cached.
 #[derive(Debug, Serialize, Deserialize)]
 pub struct Entry {
     pub attr: BlockMetaSecrets,
@@ -406,12 +441,14 @@ pub struct Entry {
     pub entry_timeout: Duration,
 }
 
+/// A request to lookup a name in a directory.
 #[derive(Serialize, Deserialize)]
 pub struct Lookup<'a> {
     pub parent: Inode,
     pub name: &'a str,
 }
 
+/// A reply containing the [Entry] stored under a name in a directory.
 #[derive(Debug, Serialize, Deserialize)]
 pub struct LookupReply {
     pub inode: Inode,
@@ -419,6 +456,7 @@ pub struct LookupReply {
     pub entry: Entry,
 }
 
+/// A request to create a file in a directory under a given name.
 #[derive(Serialize, Deserialize)]
 pub struct Create<'a> {
     pub parent: Inode,
@@ -428,6 +466,7 @@ pub struct Create<'a> {
     pub umask: u32,
 }
 
+/// A reply containing information about a newly created file.
 #[derive(Serialize, Deserialize)]
 pub struct CreateReply {
     pub inode: Inode,
@@ -435,17 +474,20 @@ pub struct CreateReply {
     pub entry: Entry,
 }
 
+/// A request to hope a file.
 #[derive(Serialize, Deserialize)]
 pub struct Open {
     pub inode: Inode,
     pub flags: Flags,
 }
 
+/// A reply which contains a handle to an open file.
 #[derive(Serialize, Deserialize)]
 pub struct OpenReply {
     pub handle: Handle,
 }
 
+/// A request to read data at a particular offset from an open file.
 #[derive(Serialize, Deserialize)]
 pub struct Read {
     pub inode: Inode,
@@ -454,11 +496,13 @@ pub struct Read {
     pub size: u64,
 }
 
+/// A reply containing data read from a file.
 #[derive(Serialize, Deserialize)]
 pub struct ReadReply<'a> {
     pub data: &'a [u8],
 }
 
+/// A request to write data to a particular offset in an open file.
 #[derive(Serialize, Deserialize)]
 pub struct Write<R> {
     pub inode: Inode,
@@ -467,17 +511,20 @@ pub struct Write<R> {
     pub data: R,
 }
 
+/// A reply containing the number of bytes written to a file.
 #[derive(Serialize, Deserialize)]
 pub struct WriteReply {
     pub written: u64,
 }
 
+/// A request to flush all data cached for an open file to durable storage.
 #[derive(Serialize, Deserialize)]
 pub struct Flush {
     pub inode: Inode,
     pub handle: Handle,
 }
 
+/// A request to read the contents of a directory.
 #[derive(Serialize, Deserialize)]
 pub struct ReadDir {
     pub inode: Inode,
@@ -490,6 +537,7 @@ pub struct ReadDir {
     pub state: u64,
 }
 
+/// A reply containing the contents of a directory.
 #[derive(Serialize, Deserialize)]
 pub struct ReadDirReply {
     pub entries: Vec<(String, DirEntry)>,
@@ -498,6 +546,7 @@ pub struct ReadDirReply {
     pub new_state: u64,
 }
 
+/// A request to create a new hard link to a file.
 #[derive(Serialize, Deserialize)]
 pub struct Link<'a> {
     pub inode: Inode,
@@ -505,29 +554,36 @@ pub struct Link<'a> {
     pub name: &'a str,
 }
 
+/// A reply containing the [Entry] of the newly created hard link.
 #[derive(Serialize, Deserialize)]
 pub struct LinkReply {
     pub entry: Entry,
 }
 
+/// A request to remove a name referring to an inode.
+/// 
+/// If the inode becomes orphaned, it is removed from durable storage.
 #[derive(Serialize, Deserialize)]
 pub struct Unlink<'a> {
     pub parent: Inode,
     pub name: &'a str,
 }
 
+/// A request to read a file's metadata.
 #[derive(Serialize, Deserialize)]
 pub struct ReadMeta {
     pub inode: Inode,
     pub handle: Option<Handle>,
 }
 
+/// A reply containing the metadata of a file.
 #[derive(Serialize, Deserialize)]
 pub struct ReadMetaReply {
     pub attrs: BlockMetaSecrets,
     pub valid_for: Duration,
 }
 
+/// A request to write the metadata of a file.
 #[derive(Serialize, Deserialize)]
 pub struct WriteMeta {
     pub inode: Inode,
@@ -537,12 +593,14 @@ pub struct WriteMeta {
     pub attrs_set: AttrsSet,
 }
 
+/// A reply containing the newly written file metadata.
 #[derive(Serialize, Deserialize)]
 pub struct WriteMetaReply {
     pub attrs: BlockMetaSecrets,
     pub valid_for: Duration,
 }
 
+/// A request to pre-allocate a given amount of space from durable storage for a file.
 #[derive(Serialize, Deserialize)]
 pub struct Allocate {
     pub inode: Inode,
@@ -551,18 +609,26 @@ pub struct Allocate {
     pub size: u64,
 }
 
+/// A request to close an open file.
 #[derive(Serialize, Deserialize)]
 pub struct Close {
     pub inode: Inode,
     pub handle: Handle,
 }
 
+/// A request to forget about an inode which was previously referenced.
+/// 
+/// This message must be sent
+/// so the server knows when it can free resources associated with the inode.
+/// If `N` [Entry] structs are sent in a reply to [Lookup] messages, then the caller must send one
+/// or more [Forget] messages such that their `count` fields add up to `N`.
 #[derive(Serialize, Deserialize)]
 pub struct Forget {
     pub inode: Inode,
     pub count: u64,
 }
 
+/// The description of a region in a file to lock.
 #[derive(Serialize, Deserialize)]
 pub struct LockDesc {
     pub offset: u64,
@@ -570,6 +636,7 @@ pub struct LockDesc {
     pub exclusive: bool,
 }
 
+/// A request to lock a region of a file.
 #[derive(Serialize, Deserialize)]
 pub struct Lock {
     pub inode: Inode,
@@ -577,12 +644,14 @@ pub struct Lock {
     pub desc: LockDesc,
 }
 
+/// A request to unlock a region of a file.
 #[derive(Serialize, Deserialize)]
 pub struct Unlock {
     pub inode: Inode,
     pub handle: Handle,
 }
 
+/// A request to add a [btlib::Readcap] to a file.
 #[derive(Serialize, Deserialize)]
 pub struct AddReadcap {
     pub inode: Inode,
@@ -590,6 +659,7 @@ pub struct AddReadcap {
     pub pub_creds: ConcretePub,
 }
 
+/// A request to give a process access to an inode.
 #[derive(Serialize, Deserialize)]
 pub struct GrantAccess {
     pub inode: Inode,

+ 9 - 0
crates/btfproto/src/server.rs

@@ -1,4 +1,9 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
+//! Code for creating filesystem servers.
+//! 
+//! The [FsProvider] trait defines the interface that a filesystem server
+//! must implement.
+//! A new server can be created using the [new_fs_server] function.
 use crate::msg::{Read as ReadMsg, *};
 
 use btlib::{crypto::Creds, BlockPath, Result};
@@ -6,6 +11,7 @@ use btmsg::{MsgCallback, MsgReceived, Receiver};
 use core::future::Future;
 use std::{net::IpAddr, ops::Deref, sync::Arc};
 
+/// Trait for types which act as filesystem servers.
 pub trait FsProvider: Send + Sync {
     type LookupFut<'c>: Send + Future<Output = Result<LookupReply>>
     where
@@ -286,6 +292,9 @@ impl<P: 'static + Send + Sync + FsProvider> MsgCallback for ServerCallback<P> {
     }
 }
 
+/// Creates a new filesystem server by creating a [Receiver] which uses the given [FsProvider] to
+/// handle filesystem access requests.
+/// The server listens on `ip_addr` and uses `creds` to authenticate itself to clients.
 pub fn new_fs_server<C, P>(ip_addr: IpAddr, creds: Arc<C>, provider: Arc<P>) -> Result<Receiver>
 where
     C: 'static + Creds,

+ 126 - 104
crates/btlib/src/crypto.rs

@@ -1,4 +1,12 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
+//! This module contains types providing the cryptographic primitives necessary to implement
+//! Blocktree.
+//! 
+//! The [openssl] create is used for all of these primitives,
+//! none of them are directly implemented in this module.
+//! Rather, the types here wrap the functionality provided by OpenSSL in a more convenient
+//! interface, and they serve as an abstraction layer, so that the underlying crypto library can be
+//! more easily replaced.
 pub mod tpm;
 
 pub mod merkle_stream;
@@ -47,6 +55,10 @@ use std::{
 use strum_macros::{Display, EnumDiscriminants, FromRepr};
 use zeroize::{ZeroizeOnDrop, Zeroizing};
 
+/// The encryption of some type `T`.
+/// 
+/// This type is just e wrapper around a [Vec] of [u8] which remembers the type it was serialized
+/// and encrypted from.
 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
 pub struct Ciphertext<T> {
     data: Vec<u8>,
@@ -54,7 +66,7 @@ pub struct Ciphertext<T> {
 }
 
 impl<T> Ciphertext<T> {
-    pub fn new(data: Vec<u8>) -> Ciphertext<T> {
+    fn new(data: Vec<u8>) -> Ciphertext<T> {
         Ciphertext {
             data,
             phantom: PhantomData,
@@ -62,6 +74,10 @@ impl<T> Ciphertext<T> {
     }
 }
 
+/// A signature over the serialization of a type `T`.
+/// 
+/// This struct allows the signature over a serialization of `T` to be stored together with a
+/// signature over  it.
 pub struct Signed<T> {
     _data: Vec<u8>,
     sig: Signature,
@@ -69,7 +85,7 @@ pub struct Signed<T> {
 }
 
 impl<T> Signed<T> {
-    pub fn new(data: Vec<u8>, sig: Signature) -> Signed<T> {
+    fn new(data: Vec<u8>, sig: Signature) -> Signed<T> {
         Signed {
             _data: data,
             sig,
@@ -189,9 +205,12 @@ impl From<ErrorStack> for Error {
     }
 }
 
+/// Contains information about why a signature verification failed.
 #[derive(Debug)]
 pub struct SignatureMismatch {
+    /// The principal whose signature was actually present.
     pub actual: Principal,
+    /// The principal whose signature was expected.
     pub expected: Principal,
 }
 
@@ -229,7 +248,7 @@ impl<T: ?Sized + Op, P: DerefMut<Target = T>> Op for P {
     }
 }
 
-/// An ongoing hash hash operation.
+/// An ongoing hash operation.
 pub trait HashOp: Op {
     /// The specific hash type which is returned by the finish method.
     type Hash: Hash;
@@ -241,7 +260,7 @@ pub trait HashOp: Op {
     fn finish(&mut self) -> Result<Self::Hash>;
 }
 
-// A hash operation which uses OpenSSL.
+/// A hash operation which uses OpenSSL.
 pub struct OsslHashOp<H> {
     hasher: Hasher,
     phantom: PhantomData<H>,
@@ -292,7 +311,7 @@ impl<H: Hash + From<DigestBytes>> HashOp for OsslHashOp<H> {
     }
 }
 
-/// A wrapper which updates a `HashOp` when data is read or written.
+/// A wrapper which updates a [HashOp] when data is read or written.
 pub struct HashStream<T, Op: HashOp> {
     inner: T,
     op: Op,
@@ -387,6 +406,7 @@ impl<A: Default, T: Hash<Arg = A>> DefaultHash for T {
     }
 }
 
+/// Represents the SHA2-256 hash algorithm.
 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hashable, Clone)]
 pub struct Sha2_256([u8; Self::LEN]);
 
@@ -445,6 +465,7 @@ impl Hash for Sha2_256 {
     }
 }
 
+/// Represents the SHA2-512 hash algorithm.
 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hashable, Clone)]
 pub struct Sha2_512(#[serde(with = "BigArray")] [u8; Self::LEN]);
 
@@ -522,6 +543,105 @@ pub enum VarHash {
     Sha2_512(Sha2_512),
 }
 
+impl VarHash {
+    /// The character that's used to separate a hash type from its value in its string
+    /// representation.
+    const HASH_SEP: char = '!';
+
+    pub fn kind(&self) -> HashKind {
+        self.into()
+    }
+
+    pub fn as_slice(&self) -> &[u8] {
+        self.as_ref()
+    }
+
+    pub fn as_mut_slice(&mut self) -> &mut [u8] {
+        self.as_mut()
+    }
+}
+
+impl From<HashKind> for VarHash {
+    fn from(kind: HashKind) -> VarHash {
+        match kind {
+            HashKind::Sha2_256 => VarHash::Sha2_256(Sha2_256::default()),
+            HashKind::Sha2_512 => VarHash::Sha2_512(Sha2_512::default()),
+        }
+    }
+}
+
+impl AsRef<[u8]> for VarHash {
+    fn as_ref(&self) -> &[u8] {
+        match self {
+            VarHash::Sha2_256(arr) => arr.as_ref(),
+            VarHash::Sha2_512(arr) => arr.as_ref(),
+        }
+    }
+}
+
+impl AsMut<[u8]> for VarHash {
+    fn as_mut(&mut self) -> &mut [u8] {
+        match self {
+            VarHash::Sha2_256(arr) => arr.as_mut(),
+            VarHash::Sha2_512(arr) => arr.as_mut(),
+        }
+    }
+}
+
+impl TryFrom<MessageDigest> for VarHash {
+    type Error = crate::Error;
+
+    fn try_from(value: MessageDigest) -> Result<Self> {
+        let kind: HashKind = value.try_into()?;
+        Ok(kind.into())
+    }
+}
+
+impl Hash for VarHash {
+    type Op = VarHashOp;
+    type Arg = HashKind;
+
+    fn new(arg: Self::Arg) -> Self {
+        arg.into()
+    }
+
+    fn kind(&self) -> HashKind {
+        self.kind()
+    }
+
+    fn start_op(&self) -> Result<Self::Op> {
+        VarHashOp::new(self.kind())
+    }
+}
+
+impl TryFrom<&str> for VarHash {
+    type Error = crate::Error;
+
+    fn try_from(string: &str) -> Result<VarHash> {
+        let mut split: Vec<&str> = string.split(Self::HASH_SEP).collect();
+        if split.len() != 2 {
+            return Err(bterr!(Error::InvalidHashFormat));
+        };
+        let second = split.pop().ok_or(Error::InvalidHashFormat)?;
+        let first = split
+            .pop()
+            .ok_or(Error::InvalidHashFormat)?
+            .parse::<usize>()
+            .map_err(|_| Error::InvalidHashFormat)?;
+        let mut hash = VarHash::from(HashKind::from_repr(first).ok_or(Error::InvalidHashFormat)?);
+        base64_url::decode_to_slice(second, hash.as_mut()).map_err(|_| Error::InvalidHashFormat)?;
+        Ok(hash)
+    }
+}
+
+impl Display for VarHash {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let hash_kind: HashKind = self.into();
+        let hash_data = base64_url::encode(self.as_ref());
+        write!(f, "{}{}{hash_data}", hash_kind as u32, VarHash::HASH_SEP)
+    }
+}
+
 #[allow(clippy::derivable_impls)]
 impl Default for HashKind {
     fn default() -> HashKind {
@@ -665,105 +785,7 @@ impl From<HashKind> for MessageDigest {
     }
 }
 
-impl VarHash {
-    /// The character that's used to separate a hash type from its value in its string
-    /// representation.
-    const HASH_SEP: char = '!';
-
-    pub fn kind(&self) -> HashKind {
-        self.into()
-    }
-
-    pub fn as_slice(&self) -> &[u8] {
-        self.as_ref()
-    }
-
-    pub fn as_mut_slice(&mut self) -> &mut [u8] {
-        self.as_mut()
-    }
-}
-
-impl From<HashKind> for VarHash {
-    fn from(kind: HashKind) -> VarHash {
-        match kind {
-            HashKind::Sha2_256 => VarHash::Sha2_256(Sha2_256::default()),
-            HashKind::Sha2_512 => VarHash::Sha2_512(Sha2_512::default()),
-        }
-    }
-}
-
-impl AsRef<[u8]> for VarHash {
-    fn as_ref(&self) -> &[u8] {
-        match self {
-            VarHash::Sha2_256(arr) => arr.as_ref(),
-            VarHash::Sha2_512(arr) => arr.as_ref(),
-        }
-    }
-}
-
-impl AsMut<[u8]> for VarHash {
-    fn as_mut(&mut self) -> &mut [u8] {
-        match self {
-            VarHash::Sha2_256(arr) => arr.as_mut(),
-            VarHash::Sha2_512(arr) => arr.as_mut(),
-        }
-    }
-}
-
-impl TryFrom<MessageDigest> for VarHash {
-    type Error = crate::Error;
-
-    fn try_from(value: MessageDigest) -> Result<Self> {
-        let kind: HashKind = value.try_into()?;
-        Ok(kind.into())
-    }
-}
-
-impl Hash for VarHash {
-    type Op = VarHashOp;
-    type Arg = HashKind;
-
-    fn new(arg: Self::Arg) -> Self {
-        arg.into()
-    }
-
-    fn kind(&self) -> HashKind {
-        self.kind()
-    }
-
-    fn start_op(&self) -> Result<Self::Op> {
-        VarHashOp::new(self.kind())
-    }
-}
-
-impl TryFrom<&str> for VarHash {
-    type Error = crate::Error;
-
-    fn try_from(string: &str) -> Result<VarHash> {
-        let mut split: Vec<&str> = string.split(Self::HASH_SEP).collect();
-        if split.len() != 2 {
-            return Err(bterr!(Error::InvalidHashFormat));
-        };
-        let second = split.pop().ok_or(Error::InvalidHashFormat)?;
-        let first = split
-            .pop()
-            .ok_or(Error::InvalidHashFormat)?
-            .parse::<usize>()
-            .map_err(|_| Error::InvalidHashFormat)?;
-        let mut hash = VarHash::from(HashKind::from_repr(first).ok_or(Error::InvalidHashFormat)?);
-        base64_url::decode_to_slice(second, hash.as_mut()).map_err(|_| Error::InvalidHashFormat)?;
-        Ok(hash)
-    }
-}
-
-impl Display for VarHash {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let hash_kind: HashKind = self.into();
-        let hash_data = base64_url::encode(self.as_ref());
-        write!(f, "{}{}{hash_data}", hash_kind as u32, VarHash::HASH_SEP)
-    }
-}
-
+/// A [HashOp] which produces a [VarHash].
 pub struct VarHashOp {
     kind: HashKind,
     hasher: Hasher,

+ 1 - 0
crates/btlib/src/error.rs

@@ -62,6 +62,7 @@ macro_rules! suppress_err_if_non_zero {
     };
 }
 
+/// The common result type used by the Blocktree crates.
 pub type Result<T> = std::result::Result<T, Error>;
 
 /// The top-level error type used by this crate. This is just a newtype wrapper around

+ 0 - 1
crates/btlib/src/lib.rs

@@ -11,7 +11,6 @@ mod block_path;
 pub mod buf_reader;
 pub mod collections;
 pub mod config_helpers;
-/// Code which enables cryptographic operations.
 pub mod crypto;
 pub mod drop_trigger;
 pub mod error;

+ 3 - 1
crates/btmsg/src/lib.rs

@@ -1,5 +1,7 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
-//! Code which enables sending messages between processes in the blocktree system.
+//! This crate contains the implementation of the secure message transport `bttp`.
+//! 
+//! A `bttp` server is implemented by [Receiver] and a client by [Transmitter].
 #![feature(impl_trait_in_assoc_type)]
 
 pub use btlib::Result;

+ 149 - 131
doc/BlocktreeDce/BlocktreeDce.tex

@@ -95,8 +95,8 @@ The cryptographic mechanisms used to implement these protections are described i
 % Protocol contracts.
 One of the design goals of Blocktree is to facilitate the creation of composable distributed
 systems.
-A major challenge to building such systems is the difficulty is locating the cause of bugs when they
-inevitably occur.
+A major challenge to building such systems is the difficulty is isolating bugs when they inevitably
+occur.
 Research into session types (a.k.a. Behavioral Types) promises to bring the safety benefits
 of type checking to actor communication (\cite{armstrong} chapter 9).
 Blocktree integrates a session typing system that allows protocol contracts to be defined that
@@ -186,6 +186,23 @@ not every delivery.
 This mechanism is intended for periodic tasks or delaying work to a later time,
 not for building hard realtime systems.
 
+% The runtime is implemented using tokio.
+The actor runtime is implemented using the Rust asynchronous runtime tokio
+[\url{https://tokio.rs}].
+Actors are spawned as tasks in the tokio runtime,
+and multi-producer single consumer channels are used for message delivery.
+Because actors are just tasks,
+they can do anything a task can do,
+including awaiting other \texttt{Future}s.
+Because of this, there is no need for the actor runtime to support short-lived worker tasks,
+as any such use-case can be accomplished by awaiting a set of \texttt{Future}s.
+This allows the runtime to focus on providing support for services.
+Using tokio also means that the actor runtime has access to a high performance multi-threaded
+runtime with evented IO.
+This asynchronous programming model ensures that resources are efficiently utilized,
+and is ideal for a system focused on orchestrating services which may be used by many clients.
+
+\subsection{Services}
 % Description of virtual actor system.
 One of the challenges in building actor systems is supervising and managing actors' lifecycles.
 This is handled in Erlang \cite{armstrong} through the use of supervision trees,
@@ -207,14 +224,56 @@ Services which directly use operating system resource,
 such as those that listen on network sockets,
 are often started immediately after registration so they're available to external clients.
 
+% Message routing to services.
+Blocktree uses services to represent a logical collection of actors which implement some kind of
+computational service for other actors in the system or external clients.
+A service is identified by a Blocktree path.
+Only one service implementation can be registered in a particular runtime,
+though this implementation may be used to spawn many actors as providers for the service,
+each associated with a different ID.
+The runtime spawns a new actor when it finds no service provider associated with the ID in  
+message it's delivering.
+Some services may only have one service provider in a given runtime,
+as is the case for the sector and filesystem services.
+The \texttt{scope} and \texttt{rootward} field in a service name specify the set of runtimes to
+which a message may be delivered.
+They allow the sender to express their intended recipient,
+while still affording enough flexibility to the runtime to route messages as needed.
+If \texttt{rootward} is \texttt{false},
+the message is delivered to a service provider in a runtime that is directly contained in
+\texttt{scope}.
+If \texttt{rootward} is \texttt{true},
+the parent directories of scope are searched,
+working towards the root of the filesystem tree,
+and the message is delivered to the first provider of \texttt{service} which is found.
+When there are multiple service providers to which a given message could be delivered,
+the one to which it is actually delivered is unspecified to allow the runtime to balance load.
+Delivery will occur to at most one recipient,
+even in the case that there are multiple potential recipients.
+In order to contact other runtimes and deliver messages to them
+their network endpoints (IP addresses and UDP ports) need to be known.
+This is achieved by maintaining a file with a runtime's endpoint in the same directory as
+the runtime.
+The runtime is granted write permissions on the file,
+and it is updated by \texttt{bttp} when it begins listening on a new endpoint.
+The port a \texttt{bttp} server uses to listen for unicast connections is uniformly
+randomly selected from the set of ports in the dynamic range (49152-65535) which are unused on the
+server's host.
+Use of a random port allows many different \texttt{bttp} servers to share a single IP address
+and makes Blocktree more resistent to censorship.
+The services which are allowed to be registered in a given runtime are specified in the runtime's
+file.
+The runtime reads this list and uses it to deny service registrations for unauthorized services.
+The list is also read by other runtime's when they're searching for service providers.
+
 % Message addressing modes.
 Messages can be addressed to services or specific actors.
-When addressing to a specific actor,
+When addressed to a specific actor,
 the message contains an \emph{actor name},
 which is a pair consisting of the path of the runtime hosting the actor and the \texttt{Uuid}
 identifying the specific actor in that runtime.
-When addressing a service,
-the message is dispatched using a \emph{service name},
+When addressed to a service,
+a message is dispatched using a \emph{service name},
 which contains the following fields:
 \begin{enumerate}
   \item \texttt{service}: The path identifying the receiving service.
@@ -239,22 +298,23 @@ The reply contains the name of the file actor spawned by the filesystem service
 file.
 Messages are then dispatched to the file actor using its actor name to read and write to the file.
 
-% The runtime is implemented using tokio.
-The actor runtime is implemented using the Rust asynchronous runtime tokio
-[\url{https://tokio.rs}].
-Actors are spawned as tasks in the tokio runtime,
-and multi-producer single consumer channels are used for message delivery.
-Because actors are just tasks,
-they can do anything a task can do,
-including awaiting other \texttt{Future}s.
-Because of this, there is no need for the actor runtime to support short-lived worker tasks,
-as any such use-case can be accomplished by awaiting a set of \texttt{Future}s.
-This allows the runtime to focus on providing support for services.
-Using tokio also means that the actor runtime has access to a high performance multi-threaded
-runtime with evented IO.
-This asynchronous programming model ensures that resources are efficiently utilized,
-and is ideal for a system focused on orchestrating services which may be used by many clients.
+% The sector and filesystem service.
+The filesystem is itself implemented as a service.
+A filesystem service provider can be passed messages to delete files, list directory contents,
+open files, or perform other standard filesystem operations.
+When a file is opened,
+a new actor is spawned which owns the newly created file handle and its name is returned to the
+caller in a reply.
+Subsequent read and write messages are sent to this actor.
+The filesystem service does not persist any data itself,
+its job is to function as an integration layer,
+conglomerating sector data from many different sources into a single unified interface.
+The sector service is what is ultimately responsible for storing data
+and maintaining the persistent state of the system.
+It stores sector data in the local filesystem of each computer on which it's registered.
+The details of how this is accomplished are described in the next section.
 
+\subsection{Transporting Messages}
 % Delivering messages over the network.
 Messages can be forwarded between actor runtimes using a secure transport called \texttt{bttp}.
 This transport is implemented using the QUIC protocol \cite{quic}, which integrates TLS for
@@ -290,6 +350,7 @@ This is possible thanks to the Rust ownership system,
 because the message sender gives ownership to the runtime when it dispatches the message,
 and the runtime gives ownership to the recipient when it delivers it.
 
+\subsection{Communication Security Model}
 % Security model based on filesystem permissions.
 A runtime is represented in the filesystem as a file.
 Among other things,
@@ -318,6 +379,7 @@ security requirements into different operating system processes,
 which ensures all of the process isolation machinery in the operating system will be used to
 isolate them.
 
+\subsection{Actor Ownership}
 % Representing resources as actors.
 As in other actor systems, it is convenient to represent resources in Blocktree using actors.
 This allows the same security model used to control communication between actors to be used for
@@ -353,64 +415,30 @@ so distributed resources can be managed by owning actors in many different runti
 In this case the connection to the remote runtime serves as a proxy for the remote owner,
 so the owned actors will be ordered to return if this connection dies or the remote owner dies.
 
-% Message routing to services.
-Blocktree uses services to represent a logical collection of actors which implement some kind of
-service for other actors in the system or external clients.
-A service is identified by a Blocktree path.
-Only one service implementation can be registered in a particular runtime,
-though this implementation may be used to spawn many actors as providers for the service,
-each associated with a different ID.
-The runtime spawns a new actor when it finds no service provider associated with the ID in  
-message it's delivering.
-Some services may only have one service provider in a given runtime,
-as is the case for the sector and filesystem services.
-The \texttt{scope} and \texttt{rootward} field in a service name specify the set of runtimes to
-which a message may be delivered.
-They allow the sender to express their intended recipient,
-while still affording enough flexibility to the runtime to route messages as needed.
-If \texttt{rootward} is \texttt{false},
-the message is delivered to a service provider in a runtime that is directly contained in
-\texttt{scope}.
-If \texttt{rootward} is \texttt{true},
-the parent directories of scope are searched,
-working towards the root of the filesystem tree,
-and the message is delivered to the first provider of \texttt{service} which is found.
-When there are multiple service providers to which a given message could be delivered,
-the one to which it is actually delivered is unspecified to allow the runtime to balance load.
-Delivery will occur to at most one recipient,
-even in the case that there are multiple potential recipients.
-In order to contact other runtimes and deliver messages to them
-their network endpoints (IP addresses and UDP ports) need to be known.
-This is achieved by maintaining a file with a runtime's endpoint in the same directory as
-the runtime.
-The runtime is granted write permissions on the file,
-and it is updated by \texttt{bttp} when it begins listening on a new endpoint.
-The port a \texttt{bttp} server uses to listen for unicast connections is uniformly
-randomly selected from the set of ports in the dynamic range (49152-65535) which are unused on the
-server's host.
-Use of a random port allows many different \texttt{bttp} servers to share a single IP address
-and makes Blocktree more resistent to censorship.
-The services which are allowed to be registered in a given runtime are specified in the runtime's
-file.
-The runtime reads this list and uses it to deny service registrations for unauthorized services.
-The list is also read by other runtime's when they're searching for service providers.
-
-% The sector and filesystem service.
-The filesystem is itself implemented as a service.
-A filesystem service provider can be passed messages to delete files, list directory contents,
-open files, or perform other standard filesystem operations.
-When a file is opened,
-a new actor is spawned which owns the newly created file handle and its name is returned to the
-caller in a reply.
-Subsequent read and write messages are sent to this actor.
-The filesystem service does not persist any data itself,
-its job is to function as an integration layer,
-conglomerating sector data from many different sources into a single unified interface.
-The sector service is what is ultimately responsible for storing data
-and maintaining the persistent state of the system.
-It stores sector data in the local filesystem of each computer on which it's registered.
-The details of how this is accomplished are described in the next section.
+% Running containers using actors.
+While the actor runtime can be a convenient way of implementing new systems,
+a backwards compatibility mechanism is needed to allow existing systems to operate in the context
+of Blocktree.
+Containers have become the standard unit of deployment for modern applications,
+which makes them both useful and straight-forward to support in Blocktree.
+To execute a container in the actor runtime,
+it must be owned by a supervising actor.
+This actor is responsible for starting the container and managing the container's kernel resources.
+Logically, it owns all such resources, including all spawned operating system processes.
+When the actor halts,
+all of these resources are destroyed.
+All network communication to the container is controlled by the supervising actor.
+The supervisor can be configured to bind container ports to host ports,
+as is commonly done today,
+but it can also be used to encapsulate traffic to and from the container in Blocktree messages.
+These messages are routed to other actors based on the configuration of the supervisor.
+This essentially creates a VPN for containers,
+ensuring that regardless of how insecure their communication is,
+they will be safe to communicate over any network.
+This network encapsulation system could be used in other actors as well,
+allowing a lightweight and secure VPN system to built.
 
+\subsection{Runtime Discovery Over the Network}
 % Runtime queries.
 While it's possible to resolve runtime paths to network endpoints when the filesystem is available,
 another mechanism is needed to allow the filesystem service providers to be discovered.
@@ -520,6 +548,7 @@ an answering runtime should check that that the execute permission is granted on
 that it's responsible for storing.
 If all these checks pass, it should forward the querier to the next runtime as usual.
 
+\subsection{Protocol Contracts}
 % Overview of protocol contracts and runtime checking of protocol adherence.
 To facilitate the creation of composable systems,
 a protocol contract checking system based on session types has been designed.
@@ -623,10 +652,11 @@ we allow for many different interoperable implementations to be created.
 We can also isolate bugs in these implementations because unexpected or malformed messages are
 checked for by the generated code.
 
+\subsection{Future Work}
 % Implementing actors in languages other than Rust.
-Today the actor runtime only supports actors implemented in Rust.
-A WebAssembly (Wasm) plugin system is planned to allow actors to be implemented in any language which can compile to
-Wasm.
+Currently, the actor runtime only supports actors implemented in Rust.
+A WebAssembly (Wasm) plugin system is planned which allows actors to be implemented in any language
+which can be compiled to Wasm.
 This work is blocked pending the standardization of the WebAssembly Component Model,
 which promises to provide an interface definition language which will allow type safe actors to be
 defined in many different languages.
@@ -634,38 +664,15 @@ Once Wasm support is added,
 it will make sense to use the filesystem to distribute compiled actor modules,
 as the strong integrity protection it provides make it an ideal way to securely distribute software.
 
-% Running containers using actors.
-While the actor runtime can be a convenient way of implementing new systems,
-a backwards compatibility mechanism is needed to allow existing systems to operate in the context
-of Blocktree.
-Containers have become the standard unit of deployment for modern applications,
-which makes them both useful and straight-forward to support in Blocktree.
-To execute a container in the actor runtime,
-it must be owned by a supervising actor.
-This actor is responsible for starting the container and managing the container's kernel resources.
-Logically, it owns all such resources, including all spawned operating system processes.
-When the actor halts,
-all of these resources are destroyed.
-All network communication to the container is controlled by the supervising actor.
-The supervisor can be configured to bind container ports to host ports,
-as is commonly done today,
-but it can also be used to encapsulate traffic to and from the container in Blocktree messages.
-These messages are routed to other actors based on the configuration of the supervisor.
-This essentially creates a VPN for containers,
-ensuring that regardless of how well secured their communication is,
-they will be safe to communicate over any network.
-This network encapsulation system could be used in other actors as well,
-allowing a lightweight and secure VPN system to built.
-
 % Web GUI used for managing the system.
 Any computer system of even moderate complexity needs an interface for viewing and controlling the
 state of the system.
 The modern cross-platform way to accomplish this is by creating a web GUI.
-Blocktree includes a service called \texttt{btconsole} which provides such an interface.
-It can be used to view and modify the filesystem, modify runtime attributes,
+Blocktree will include a service called \texttt{btconsole} which provides such an interface.
+It will be used to view and modify the filesystem, modify runtime attributes,
 and even register new services.
 The aim is to provide an interface which makes complicated network management tasks simple,
-and make networks more secure by providing a single pane of glass showing their configuration.
+and make networks more secure by providing a single pane of glass which shows their configuration.
 
 
 \section{Filesystem}
@@ -687,6 +694,7 @@ the cost of providing these protections is amortized over the size of the sector
 Thus there is tradeoff between latency and throughput when selecting the sector size:
 a smaller size means less latency but a larger one enables more throughput.
 
+\subsection{The Sector Service}
 % Types of sectors: metadata, integrity, and data.
 A file has a single metadata sector, a Merkle sector, and zero or more data sectors.
 The sector size of a file can be specified when it's created,
@@ -734,7 +742,7 @@ When a sector is updated,
 a new local file is created with a different name containing the new contents.
 Rather than deleting the old sector file,
 it is overwritten by the creation of a hardlink to the new file,
-and the name that used to create the new file is unlinked.
+and the name that was used to create the new file is unlinked.
 This method ensures that the sector file is updated in one atomic operation.
 The sector service also uses the local filesystem to persist the replicated log it uses for Raft.
 This file serves as a journal of sector operations.
@@ -761,7 +769,8 @@ Communication with the sector service is done by passing it messages of type \te
   }
 \end{verbatim}
 Here \texttt{FileMeta} is the type used to store metadata for files.
-Note that updated metadata is required to be sent when a sector's contents are modified.
+Note that updated metadata is required to be sent when a sector's contents are modified, because it
+contains updated integrity information.
 
 % Scaling horizontally: using Raft to create consensus cluster. Additional replication methods.
 A generation of sector service providers uses the Raft protocol to synchronize the state of the
@@ -809,7 +818,7 @@ The process it uses to do this involves several steps:
     The write message is considered valid if and only if there is a match.
 \end{enumerate}
 This same logic is used by file actors to verify the data they read from the sector service,
-except they don't modify the Merkle tree,
+except they don't modify the Merkle tree during verification,
 they just compare computed hashes to those contained in the nodes on the path from the sector's leaf
 node to the root.
 Only once a write message is validated is it shared with the sector service provider's peers in
@@ -821,6 +830,7 @@ To prevent this, a sector service provider checks a file's metadata to verify th
 principal actually has a readcap (to be defined in the next section) for the file.
 This ensures that only principals that are authorized to read a file can access its sectors.
 
+\subsection{The Filesystem Service}
 % File actors are responsible for cryptographic operations. Client-side encryption.
 The sector service is relied upon by the filesystem service to read and write sectors.
 Filesystem service providers communicate with the sector service to open files and perform
@@ -876,6 +886,7 @@ The file actor is configured when it is spawned to allow read only, write only,
 access to a file,
 depending on what type of access was requested by the actor opening the file.
 
+\subsection{Filesystem Event Publishing}
 % Streaming replication.
 Often when building distributed systems it's convenient to publish information about events to any
 interested party.
@@ -890,7 +901,7 @@ a simple notification system can be built.
 Because the contents of a directory may be distributed over many different generations,
 this system does not support the recursive monitoring of directories.
 Although this system lacks the power of \texttt{inotify} in the Linux kernel,
-it does provides some of its benefits without incurring much or a performance overhead
+it does provides some of its benefits without incurring much of a performance overhead
 or implementation complexity.
 For example, this system can be used to implement streaming replication.
 This is done by subscribing to writes on all the files that are to be replicated,
@@ -898,17 +909,7 @@ then reading new sectors as soon as notifications are received.
 These sectors can then be written into replica files in a different directory.
 This ensures that the contents of the replicas will be updated in near real-time.
 
-% Peer-to-peer distribution of sector data.
-Because of the strong integrity protection afforded to sectors,
-it is possible for peer-to-peer distribution of sector data to be done securely.
-Implementing this mechanism is planned as a future enhancement to the system.
-The idea is to base the design on bit torrent,
-where the generation responsible for a file acts as a tracker for that file,
-and the file actors accessing it communicate with one another directly using the information
-provided by the sector service.
-This could allow the system to scale elastically to a much larger number of concurrent reads by
-reducing the load on the sector service.
-
+\subsection{External Access to the Filesystem}
 % The FUSE daemon.
 Being able to access the filesystem from actors allows a programmer to implement new applications
 using Blocktree,
@@ -928,12 +929,25 @@ This would reduce the overhead associated with context switching back and forth
 to kernel space,
 increasing the performance of the system.
 
+\subsection{Future Work}
+% Peer-to-peer distribution of sector data.
+Because of the strong integrity protection afforded to sectors,
+it is possible for peer-to-peer distribution of sector data to be done securely.
+Implementing this mechanism is planned as a future enhancement to the system.
+The idea is to base the design on bit torrent,
+where the generation responsible for a file acts as a tracker for that file,
+and the file actors accessing it communicate with one another directly using the information
+provided by the sector service.
+This could allow the system to scale elastically to a much larger number of concurrent reads by
+reducing the load on the sector service.
+
 
 \section{Cryptography}
 This section describes the cryptographic mechanisms used to integrity and confidentiality protect
 files as well as procedures for obtaining credentials.
 These mechanisms are based on well-established cryptographic constructions.
 
+\subsection{Integrity Protection}
 % Integrity protection.
 A file is integrity protected by a digital signature over its metadata.
 The metadata contains an integrity field which contains the root node of the Merkle tree over
@@ -948,7 +962,7 @@ A file's metadata also contains a certificate chain,
 and this chain is used to authenticate the signature over the metadata.
 In Blocktree, the certificate chain is referred to as a \emph{writecap}
 because it grants the capability to write to files.
-This term comes from the Tahoe Least-Authority Filesystem \cite{tahoe}.
+This term comes from Tahoe: The Least-Authority Filesystem \cite{tahoe}.
 The certificates in a valid writecap are ordered by their paths,
 the initial certificate contains the longest path,
 the path in each subsequent certificate must be a prefix of the one preceding it,
@@ -961,6 +975,7 @@ By including all the information necessary to verify the integrity of a file in
 it is possible for a requestor who only knows the path of a file to verify that the contents of the
 file are authentic.
 
+\subsection{Confidentiality Protection}
 % Confidentiality protecting files with readcaps. Single pubkey operation to read a dir tree.
 Confidentiality protection of files is optional but when it is enabled,
 a file's sectors are individually encrypted using a symmetric cipher.
@@ -1014,6 +1029,7 @@ By forcing the user to create a new file,
 they are forced to re-encrypt the data using a fresh key and IV,
 which cannot be known to the principal whose readcap was revoked.
 
+\subsection{Obfuscation of Sector Files in the Local Filesystem}
 % Obfuscating sector files stored in the local filesystem.
 From an attacker's perspective,
 not every file in a domain is equally interesting.
@@ -1032,6 +1048,7 @@ This simple method makes it more difficult for an attacker to identify the files
 to
 while still allowing the sector service efficient access.
 
+\subsection{Credential Storage and Provisioning}
 % Credential stores.
 Processes need a way to securely store their credentials.
 They accomplish this by using a credential store,
@@ -1134,6 +1151,7 @@ The first runtime is configured to host the sector and filesystem services,
 so that subsequent runtimes will have access to the filesystem.
 After that, additional runtimes on the same LAN can be provisioned using the automatic process.
 
+\subsection{Access Control Examples}
 % Setting up user based access control.
 Up till now the focus has been on authentication and authorization of processes,
 but it bears discussing how user based access control can be accomplished with Blocktree.
@@ -1162,7 +1180,7 @@ Note that this setup does require all of the user's runtimes to be able to commu
 runtime whose principal was issued the readcap.
 
 % Example of how these mechanisms allow data to be shared.
-To illustrate how these mechanisms can be used to facilitate collaboration between enterprises,
+To illustrate how Blocktree can be used to enable collaboration between enterprises,
 consider a situation where two companies wish to partner on the development of a product.
 To facilitate their collaboration,
 they want to have a way to securely share data.
@@ -1188,7 +1206,7 @@ directory, by monitoring a directory for changes,
 it's possible to begin monitoring files as soon as they're created.
 
 
-\section{Examples}
+\section{Example Systems}
 This section contains examples of systems that could be built using Blocktree.
 The hope is to illustrate how this platform can be used to implement existing applications more
 easily and to make it possible to implement systems which are currently out of reach.
@@ -1465,7 +1483,7 @@ each of which is stored in its own directory.
 A single top-level directory represents the entire planet,
 and contains a manifest describing it.
 This manifest specifies the planet's name, its radius, its rotational period,
-the size of its regions in MB, as well as any
+the size limit of its regions in MB, as well as any
 other global attributes.
 This top-level directory also contains the texture for the sky box to render the view of
 space from the planet.
@@ -1478,7 +1496,7 @@ centerline parallel to the $\phi$ axis, and the one parallel to the $\lambda$ ax
 In other words, it is divided in half north to south and east to west.
 The four new regions are stored in four subdirectories of the original region's directory
 named SE, SW, NE, and NW, depending on their location relative to the original region.
-The data in the old region is then moved into the appropriate directory.
+The data in the old region is then moved into the appropriate directories.
 The directory tree of a planet essentially forms a quadtree,
 albeit one which is built up progressively.
 
@@ -1500,8 +1518,8 @@ This is allowed in a controlled way by defining plot objects.
 A plot is like a symbolic link,
 it points to a file whose contents contain the data used to render the plot.
 This mechanisms allows the owner of the planet to delegate a specific area on its surface
-to another player by creating a plot defining that area and pointing it to a file owned by that
-player.
+to another player by creating a plot defining that area and pointing it to a file owned by the
+other player.
 The other player can then write meshes, textures, and shaders into this file to describe the
 contents of the plot.
 If the other player wishes to collaborate with others on the construction,
@@ -1522,7 +1540,7 @@ the planet's surface.
 
 % Sharding planet data.
 By dividing the planet's data into different leaf directories,
-it becomes possible to provision computers running the sector and filesystem services ie each of
+it becomes possible to provision computers running the sector and filesystem services in each of
 them.
 This divides the storage and bandwidth requirements for serving the planet over this set of
 servers.
@@ -1532,14 +1550,14 @@ Game clients address their messages using the directory of the region their play
 in, and set \texttt{rootward} to true.
 These messages are delivered to the closest game server to the region the player is in,
 which may be located in the region's directory or higher up the tree.
-When a player approaches the board of a region,
+When a player approaches the border of a region,
 its game client begins dispatching messages to the adjacent directories as well.
 
 
 \section{Conclusion}
 % Blocktree serves as the basis for building distributed Unix.
 There have been many attempts to create a distributed Unix over the years.
-Time has shown that this is a very hard problem,
+Time has shown that this is a difficult problem,
 but time has not diminished its importance.
 IT systems are more complex now than ever,
 with many layers of abstraction that have built up over time.

+ 4 - 2
doc/build-docs.sh

@@ -1,6 +1,8 @@
 #!/bin/sh
 # Builds the docs for this repo. Open "target/docs/doc/README.html" in a browser to view them.
 # NOTE: This script must be run from the "./doc" subdirectory of the repo.
+# You can have cargo rebuild the rustdocs whenever the source code changes with:
+# cargo watch -s 'cargo doc --no-deps'
 set -e
 
 RUSTDOCFLAGS="--index-page $PWD/../README.md -Zunstable-options --markdown-css rustdoc/rust.css --markdown-no-toc"\
@@ -11,6 +13,6 @@ cp doc/rust.css website/static/rustdoc/
 cd doc/BlocktreeDce
 pdflatex BlocktreeDce.tex
 cp BlocktreeDce.pdf ../../website/static/
-cd -
-cd website
+cd ../../website
+rm -rf public/
 hugo

+ 2 - 2
website/content/en/_index.html

@@ -23,8 +23,8 @@ linkTitle = "Blocktree"
 
 
 {{% blocks/lead color="primary" %}}
-Blocktree is still under active development. __Do not__ use it to protect sensitive information
-or run mission critical services. 
+Blocktree solves problems that all microservices applications have to overcome, allowing
+developers to focus on their applications.
 {{% /blocks/lead %}}
 
 

+ 1 - 1
website/deploy.sh

@@ -9,4 +9,4 @@
 # https://www.linode.com/docs/products/storage/object-storage/guides/s3cmd/
 # The guide for deploying a Hugo site is also useful:
 # https://www.linode.com/docs/guides/host-static-site-object-storage/
-s3cmd --no-mime-magic --acl-public --delete-removed --delete-after sync public/ s3://blocktree
+s3cmd --no-mime-magic --acl-public --delete-removed --delete-after sync public/ s3://www.blocktree.systems