diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..c92c067
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,29 @@
+# doip.js
+
+doip.js allows browsers and Node.js projects to verify decentralized online identities based on OpenPGP.
+
+## Features
+
+- Verify online identities using profile URLs
+- Regex-based service provider detection
+- [Mocha](https://mochajs.org/) tests
+
+## Todo
+
+- Fetch keys using key servers and WKD
+
+## About Keyoxide
+
+[Keyoxide](https://keyoxide.org/), made by Yarmo Mackenbach, is a modern, secure and privacy-friendly platform to establish decentralized online identities using a novel concept know as [DOIP](doip.md). In an effort to make this technology accessible for other projects and stimulate the emergence of both complementary and competing projects, this project-agnostic library is [published on codeberg.org](https://codeberg.org/keyoxide/doipjs) and open sourced under the [Apache-2.0](https://codeberg.org/keyoxide/doipjs/src/branch/main/LICENSE) license.
+
+## Community
+
+There's a [Keyoxide Matrix room](https://matrix.to/#/#keyoxide:matrix.org) where we discuss everything DOIP and Keyoxide.
+
+## Donate
+
+Please consider [donating](https://liberapay.com/Keyoxide/) if you think this project is a step in the right direction for the internet.
+
+## Funding
+
+This library was realized with funding from [NLnet](https://nlnet.nl/project/Keyoxide/).
diff --git a/docs/_coverpage.md b/docs/_coverpage.md
new file mode 100644
index 0000000..11014ab
--- /dev/null
+++ b/docs/_coverpage.md
@@ -0,0 +1,8 @@
+# doip.js 0.3.0
+
+Decentralized OpenPGP Identity Proofs library in Node.js
+
+[Source code](https://codeberg.org/keyoxide/doipjs)
+[Getting started](#doipjs)
+
+![color](#c9beff)
diff --git a/docs/_sidebar.md b/docs/_sidebar.md
new file mode 100644
index 0000000..b95b70e
--- /dev/null
+++ b/docs/_sidebar.md
@@ -0,0 +1,20 @@
+- Getting started
+ - [Quick start](quickstart.md)
+ - [Configuration](configuration.md)
+
+- Reference
+ - API
+ - [Service provider data object](serviceproviderdataobject.md)
+
+- Concepts
+ - [DOIP](doip.md)
+ - [Cryptographic keys](#)
+ - [Proofs](#)
+ - [Claims](#)
+ - [Service providers](#)
+
+- Service providers
+ - [Liberapay](serviceproviders/liberapay.md)
+
+- More
+ - [Changelog](changelog.md)
diff --git a/docs/changelog.md b/docs/changelog.md
new file mode 100644
index 0000000..70b7569
--- /dev/null
+++ b/docs/changelog.md
@@ -0,0 +1,22 @@
+# Changelog
+
+## 0.3.0
+
+[2020-11-04](https://codeberg.org/keyoxide/doipjs/releases/tag/0.3.0)
+
+### Added
+- Liberapay service provider
+- Proxy request handler
+
+### Changed
+- Improve handling of arrays in JSON
+- Customizable proxy hostname
+
+### Fixed
+- Dots in URL regex
+
+## 0.2.0
+
+[2020-11-03](https://codeberg.org/keyoxide/doipjs/releases/tag/0.2.0)
+
+Initial release
diff --git a/docs/doip.md b/docs/doip.md
new file mode 100644
index 0000000..deafcee
--- /dev/null
+++ b/docs/doip.md
@@ -0,0 +1 @@
+# Decentralized OpenPGP Identity Proofs
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 0000000..4d9a7f3
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,28 @@
+
+
+
+
+ doip.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/quickstart.md b/docs/quickstart.md
new file mode 100644
index 0000000..272aea9
--- /dev/null
+++ b/docs/quickstart.md
@@ -0,0 +1,47 @@
+# Quick start
+
+## Installation
+
+Install using **Yarn**:
+
+```bash
+yarn add doipjs
+```
+
+Install using **NPM**:
+
+```bash
+npm install --save doipjs
+```
+
+## Usage (Node.js)
+
+Basic example:
+
+```javascript
+const doip = require('doip')
+
+const verifyIdentity = async (url, fp) => {
+ console.log(await doip.verify(url, fp))
+}
+verifyIdentity('dns:doip.rocks', '9f0048ac0b23301e1f77e994909f6bd6f80f485d')
+```
+
+This snippet works en will verify the [doip.rocks](https://doip.rocks) as
+linked to Yarmo's cryptographic key using the [dns](serviceproviders/dns.md)
+service provider.
+
+## Usage (browser)
+
+Basic example:
+
+```html
+
+```
+
+```javascript
+const verifyIdentity = async (url, fp) => {
+ console.log(await doip.verify(url, fp))
+}
+verifyIdentity('dns:doip.rocks', '9f0048ac0b23301e1f77e994909f6bd6f80f485d')
+```
diff --git a/docs/serviceproviderdataobject.md b/docs/serviceproviderdataobject.md
new file mode 100644
index 0000000..5958e37
--- /dev/null
+++ b/docs/serviceproviderdataobject.md
@@ -0,0 +1,67 @@
+# Service provider data object
+
+The object returned by any service provider has the following layout:
+
+```
+serviceprovider
+ type string: the service provider's type [web, communication]
+ name string: the service provider's name
+profile
+ display string: the profile's identifier for display
+ uri string: the profile's URI
+proof
+ uri string: the URI containing the proof to be by humans and machines
+ fetch string: an alternative URI that should be used by machines
+ useProxy boolean: should the request be sent using a proxy
+ format string: [json, text]
+claim
+ fingerprint string: the fingerprint that verifies the claim if found in the proof
+ format string: how is the fingerprint formatted [uri, message, fingerprint]
+ path array: the path to the claim inside the proof JSON
+ relation string: how the claim format relates to the proof format [contains, equals, oneOf]
+qr string: a URI to be displayed as QR code if the claim is verified
+customRequestHandler function: handles the request if the default request handler does not suffice; optional
+```
+
+## serviceprovider.type
+
+```
+Type: string
+Values: web, communication
+Mandatory: true
+```
+
+Currently, only two types of service providers are supported:
+- `web`: traditional website platforms
+- `communication`: platforms for interpersonal communication
+
+## serviceprovider.name
+
+```
+Type: string
+Values: *
+Mandatory: true
+```
+
+The name of the service provider (or protocol): `dns`, `xmpp`, `gitea`, `fediverse`, etc.
+
+## profile.display
+
+```
+Type: string
+Values: *
+Mandatory: true
+```
+
+The account's name to display
+
+## proof.fetch
+
+Sometimes, the URI used by humans to verify a claim is inadequate for use by machines. This is needed when the JSON is served by a different endpoint. In this case, machines will use a different URI than the one shown to humans.
+
+## claim.format
+
+There are three claim formats:
+- uri: the claim is formulated as `openpgp4fpr:FINGEPRPRINT`
+- message: the claim is formulated as `[Verifying my OpenPGP key: openpgp4fpr:FINGEPRPRINT]`
+- fingerprint: the claim is formulated as `FINGEPRPRINT`
diff --git a/docs/serviceproviders/liberapay.md b/docs/serviceproviders/liberapay.md
new file mode 100644
index 0000000..3f36a6f
--- /dev/null
+++ b/docs/serviceproviders/liberapay.md
@@ -0,0 +1 @@
+# Liberapay
diff --git a/package.json b/package.json
index 9d279e0..6b40e51 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,7 @@
"license:check": "./node_modules/license-check-and-add/dist/src/cli.js check",
"license:add": "./node_modules/license-check-and-add/dist/src/cli.js add",
"license:remove": "./node_modules/license-check-and-add/dist/src/cli.js remove",
+ "docs": "docsify serve ./docs",
"test": "./node_modules/mocha/bin/mocha"
},
"repository": {