diff --git a/.travis.yml b/.travis.yml
index 0518b2e9..e06ef78b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -40,6 +40,8 @@ after_success:
if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_PULL_REQUEST" = "false" && "$TRAVIS_BRANCH" == "master" && "$TRAVIS_PYTHON_VERSION" == "3.6" ]]; then
cargo doc --no-deps &&
echo "" > target/doc/index.html &&
+ cargo install mdbook &&
+ cd guide && mdbook build -d ../target/doc/guide && cd .. &&
git clone https://github.com/davisp/ghp-import.git &&
./ghp-import/ghp_import.py -n -p -f -m "Documentation upload" -r https://"$GH_TOKEN"@github.com/"$TRAVIS_REPO_SLUG.git" target/doc &&
echo "Uploaded documentation"
diff --git a/guide/.gitignore b/guide/.gitignore
new file mode 100644
index 00000000..7585238e
--- /dev/null
+++ b/guide/.gitignore
@@ -0,0 +1 @@
+book
diff --git a/guide/book.toml b/guide/book.toml
new file mode 100644
index 00000000..4f0c4ac3
--- /dev/null
+++ b/guide/book.toml
@@ -0,0 +1,3 @@
+title = "PyO3 user guide"
+description = "PyO3 user guide"
+author = "PyO3 Project and Contributors"
diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md
new file mode 100644
index 00000000..f3100a60
--- /dev/null
+++ b/guide/src/SUMMARY.md
@@ -0,0 +1,13 @@
+# Summary
+
+[Overview](./overview.md)
+
+[Getting Started](./getting_started.md)
+
+- [Type Conversions](./conversions.md)
+- [Python Exception](./exception.md)
+- [Python Module](./module.md)
+- [Python Function](./function.md)
+- [Python Class](./class.md)
+- [Parallelism](./parallelism.md)
+- [Distribution](./distribution.md)
diff --git a/guide/src/class.md b/guide/src/class.md
new file mode 100644
index 00000000..97dedb35
--- /dev/null
+++ b/guide/src/class.md
@@ -0,0 +1,3 @@
+# Python Class
+
+TODO
diff --git a/guide/src/conversions.md b/guide/src/conversions.md
new file mode 100644
index 00000000..d39c56a2
--- /dev/null
+++ b/guide/src/conversions.md
@@ -0,0 +1,17 @@
+# Type Conversions
+
+`PyO3` provides some handy traits to convert between Python types and Rust types.
+
+## `ToPyObject` and `IntoPyObject` trait
+
+`ToPyObject` trait is a conversion trait that allows various objects to be converted into `PyObject`.
+
+TODO
+
+## `IntoPyTuple` trait
+
+TODO
+
+## `FromPyObject` and `RefFromPyObject` trait
+
+TODO
diff --git a/guide/src/distribution.md b/guide/src/distribution.md
new file mode 100644
index 00000000..7034ef97
--- /dev/null
+++ b/guide/src/distribution.md
@@ -0,0 +1,13 @@
+# Distribution
+
+## `setuptools-rust` integration
+
+TODO
+
+## Source distribution
+
+TODO
+
+## Binary wheel distribution
+
+TODO
diff --git a/guide/src/exception.md b/guide/src/exception.md
new file mode 100644
index 00000000..d6e653c4
--- /dev/null
+++ b/guide/src/exception.md
@@ -0,0 +1,41 @@
+# Python Exception
+
+## Define a new exception
+
+You can use the `py_exception!` macro to define a new excetpion type:
+
+```rust
+py_exception!(module, MyError);
+```
+
+* `module` is the name of the containing module.
+* `MyError` is the name of the new exception type.
+
+For example:
+
+```rust
+#[macro_use] extern crate pyo3;
+
+use pyo3::{Python, PyDict};
+
+py_exception!(mymodule, CustomError);
+
+fn main() {
+let gil = Python::acquire_gil();
+ let py = gil.python();
+ let ctx = PyDict::new(py);
+
+ ctx.set_item(py, "CustomError", py.get_type::()).unwrap();
+
+ py.run("assert str(CustomError) == \"\"", None, Some(&ctx)).unwrap();
+ py.run("assert CustomError('oops').args == ('oops',)", None, Some(&ctx)).unwrap();
+}
+```
+
+## Raise an exception
+
+TODO
+
+## Check exception type
+
+TODO
diff --git a/guide/src/function.md b/guide/src/function.md
new file mode 100644
index 00000000..14918c99
--- /dev/null
+++ b/guide/src/function.md
@@ -0,0 +1,3 @@
+# Python Function
+
+TODO
diff --git a/guide/src/getting_started.md b/guide/src/getting_started.md
new file mode 100644
index 00000000..832e8e53
--- /dev/null
+++ b/guide/src/getting_started.md
@@ -0,0 +1,17 @@
+# Getting Started
+
+In this tutorial, we will walk through the steps of building a simple Python extension called `TODO`.
+
+## Install Rust
+
+Before we begin, we need to install Rust using the [rustup](https://www.rustup.rs/) installer:
+
+```bash
+curl https://sh.rustup.rs -sSf | sh
+```
+
+If you already have rustup installed, run this command to ensure you have the latest version of Rust:
+
+```bash
+rustup update
+```
diff --git a/guide/src/module.md b/guide/src/module.md
new file mode 100644
index 00000000..15faaa91
--- /dev/null
+++ b/guide/src/module.md
@@ -0,0 +1,3 @@
+# Python Module
+
+TODO
diff --git a/guide/src/overview.md b/guide/src/overview.md
new file mode 100644
index 00000000..d9607376
--- /dev/null
+++ b/guide/src/overview.md
@@ -0,0 +1,104 @@
+# Overview
+
+[![Build Status](https://travis-ci.org/PyO3/PyO3.svg?branch=master)](https://travis-ci.org/PyO3/PyO3)
+[![Latest Version](https://img.shields.io/crates/v/pyo3.svg)](https://crates.io/crates/pyo3)
+[![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](http://pyo3.github.io/PyO3/pyo3/)
+
+PyO3 is a [Rust](http://www.rust-lang.org/) bindings for the [Python](https://www.python.org/) interpreter.
+
+Supported Python versions:
+
+* Python2.7, Python 3.5 and up
+
+Supported Rust version:
+
+* Rust 1.17.0-nightly or later
+* On Windows, we require rustc 1.17.0-nightly
+
+## Usage
+
+To use `pyo3`, add this to your `Cargo.toml`:
+
+```toml
+[dependencies]
+pyo3 = "0.1"
+```
+
+Example program displaying the value of `sys.version`:
+
+```rust
+extern crate pyo3;
+
+use pyo3::{Python, PyDict, PyResult};
+
+fn main() {
+ let gil = Python::acquire_gil();
+ hello(gil.python()).unwrap();
+}
+
+fn hello(py: Python) -> PyResult<()> {
+ let sys = py.import("sys")?;
+ let version: String = sys.get("version")?.extract(py)?;
+
+ let locals = PyDict::new(py);
+ locals.set_item("os", py.import("os")?)?;
+ let user: String = py.eval("os.getenv('USER') or os.getenv('USERNAME')", None, Some(&locals))?.extract(py)?;
+
+ println!("Hello {}, I'm Python {}", user, version);
+ Ok(())
+}
+```
+
+Example library with python bindings:
+
+The following two files will build with `cargo build`, and will generate a python-compatible library.
+On Mac OS, you will need to rename the output from \*.dylib to \*.so.
+On Windows, you will need to rename the output from \*.dll to \*.pyd.
+
+**`Cargo.toml`:**
+
+```toml
+[lib]
+name = "rust2py"
+crate-type = ["cdylib"]
+
+[dependencies.pyo3]
+version = "0.1"
+features = ["extension-module"]
+```
+
+**`src/lib.rs`**
+
+```rust
+#![feature(proc_macro)]
+
+extern crate pyo3;
+use pyo3::{py, PyResult, Python, PyModule};
+
+// add bindings to the generated python module
+// N.B: names: "librust2py" must be the name of the `.so` or `.pyd` file
+#[py::modinit(rust2py)]
+fn init_mod(py: Python, m: &PyModule) -> PyResult<()> {
+ m.add(py, "__doc__", "This module is implemented in Rust.")?;
+
+ #[pyfn(m, "sum_as_string")]
+ // pyo3 aware function. All of our python interface could be declared in a separate module.
+ // Note that the `#[pyfn()]` annotation automatically converts the arguments from
+ // Python objects to Rust values; and the Rust return value back into a Python object.
+ fn sum_as_string_py(_: Python, a:i64, b:i64) -> PyResult {
+ let out = sum_as_string(a, b);
+ Ok(out)
+ }
+
+ Ok(())
+}
+
+// logic implemented as a normal rust function
+fn sum_as_string(a:i64, b:i64) -> String {
+ format!("{}", a + b).to_string()
+}
+
+# fn main() {}
+```
+
+For `setup.py` integration, see [setuptools-rust](https://github.com/PyO3/setuptools-rust)
diff --git a/guide/src/parallelism.md b/guide/src/parallelism.md
new file mode 100644
index 00000000..ee3df3b4
--- /dev/null
+++ b/guide/src/parallelism.md
@@ -0,0 +1,3 @@
+# Parallelism
+
+TODO