diff --git a/doc/guide/asciidoc/Makefile b/doc/guide/asciidoc/Makefile
new file mode 100644
index 000000000000..0edf8345bb72
--- /dev/null
+++ b/doc/guide/asciidoc/Makefile
@@ -0,0 +1,12 @@
+all: render_html render_man
+
+render_html:
+ mkdir -p output/html
+ asciidoctor -b html5 -D output/html cockpit-guide.adoc
+
+render_man:
+ mkdir -p output/html
+ asciidoctor -b manpage -D output/man ./man/*.adoc
+
+clean:
+ rm -rf output/
diff --git a/doc/guide/asciidoc/api-base1.adoc b/doc/guide/asciidoc/api-base1.adoc
new file mode 100644
index 000000000000..b332bbe2c86b
--- /dev/null
+++ b/doc/guide/asciidoc/api-base1.adoc
@@ -0,0 +1 @@
+This package contains basic support API available to other packages.
diff --git a/doc/guide/asciidoc/api-cockpit.adoc b/doc/guide/asciidoc/api-cockpit.adoc
new file mode 100644
index 000000000000..3d61bde3cc52
--- /dev/null
+++ b/doc/guide/asciidoc/api-cockpit.adoc
@@ -0,0 +1,14 @@
+cockpit.js
+
+cockpit.js
+
+Basic cockpit API to interact with the system
+
+[[api-cockpit-loading]]
+== Loading cockpit.js
+
+`cockpit.js` should be loaded via a script tag.
+
+....
+
+....
+
+Do not assume you can link to any file in any other package. Refer to
+the link:#development[list of API packages] for those that are available
+for use.
+
+[[package-minified]]
+=== Content Negotiation
+
+In order to support gzipped and/or minified data, the files in a package
+are loaded using content negotiation logic. A HTTP request for the file
+`test.js` in the package named `mypackage` will return
+`mypackage/test.js` or `mypackage/test.js.gz` (in undefined preference).
+If neither exists, then it returns `mypackage/test.js.min` or
+`mypackage/test.js.min.gz` (again in undefined preference).
+
+When packages are loaded from a system directory, Cockpit optimizes the
+file system lookups above, by pre-listing the files. This is one of the
+reasons that you should never change packages installed to a system
+directory while Cockpit is running.
+
+[[package-api]]
+=== Using Cockpit API
+
+Cockpit has API available for writing packages. There is no API
+available for external callers to invoke via HTTP, REST or otherwise.
+
+API from various packages can be used to implement Cockpit packages.
+Each package listed here has some API available for use. Only the API
+explicitly documented should be used.
+
+* link:#development[API Listing]
+
+To include javascript from the API, simply load it into your HTML using
+a script tag. Alternatively you can use an javascript loader.
+
+[[package-bridges]]
+=== Bridges for specific tasks
+
+On the server side the link:#cockpit-bridge.1[`cockpit-bridge`] connects
+to various system APIs that the front end UI requests it to. There are
+additional bridges for specific tasks that the main `cockpit-bridge`
+cannot handle, such as using the PCP C library API.
+
+These additional bridges can be registered in a `"bridges"` section of a
+package's `manifest.json` file. Building such a bridge is a complex
+tasks, and we will skip over that here. However it is useful to adjust
+how these additional bridges are called, and so we'll look at how they
+are registered.
+
+An example `manifest.json` with a bridges section:
+
+....
+{
+ "bridges": [
+ {
+ "match": { "payload": "metrics1" },
+ "spawn": [ "/usr/libexec/cockpit-pcp" ]
+ }
+ ]
+}
+....
+
+The bridges are considered in the order they are listed in the array.
+Use the `manifest.json``"priority"` field to control order between
+packages. The bridges are registered using JSON objects that have the
+following properties:
+
+environ::
+ Optional, additional environment variables to pass to the bridge
+ command.
+match::
+ The `"match"` object describes which channel open command options need
+ to match for a given channel to be handed over to this bridge.
+privileged::
+ If set to `true`, this marks the bridge as a superuser bridge. Cockpit
+ will start one of these explicitly when trying to escalate the
+ privileges of a session. A privileged bridge can not have a `"match"`
+ property.
+label::
+ Setting this enables selection of privileged bridges in the UI. When
+ no privileged bridge has a `label`, then Cockpit will start the bridge
+ that runs `sudo`. This is the case in a default Cockpit installation.
+ When at least one privileged bridge has a `label` then the user can
+ select one of them when escalating privileges. As a special case, if
+ only one bridge has a `label`, then the step of selecting a bridge is
+ omitted in the UI and that one bridge is always started.
+ +
+ Thus, if you add a privileged bridge with a `label` in a new manifest,
+ Cockpit will use that bridge the next time a user opens the
+ "Administrative access" dialog. If you want the user to choose between
+ the `sudo` method and your new one, you need to duplicate the `sudo`
+ bridge definition in your manifest and give it a label.
+problem::
+ If a problem is specified, and this bridge fails to start up then
+ channels will be closed with this problem code. Otherwise later
+ bridges or internal handlers for the channel will be invoked.
+spawn::
+ The command and arguments to invoke.
+
+The `spawn` and `environ` values can be dynamically taken from a
+matching open command values. When a value in either the `spawn` or
+`environ` array contains a named variable wrapped in `${}`, the variable
+will be replaced with the value contained in the matching open command.
+Only named variables are supported and name can only contain letters,
+numbers and the following symbols: `._-`
+
+For example a bridges section like:
+
+....
+{
+ "bridges": [
+ {
+ "match": { "payload": "example" },
+ "environ": [ "TAG=${tag}" ],
+ "spawn: [ "/example-bridge", "--tag", "${tag}" ],
+ "problem": "access-denied"
+ }
+ ]
+}
+....
+
+when a open command is received with a payload of `example` with `tag`
+value of `tag1`. The following command will be spawned
+
+....
+TAG=tag1 /example-bridge --tag tag1
+....
+
+Processes that are reused so if another open command with a "tag" of
+`tag1` is received. The open command will be passed to existing process,
+rather than spawning a new one. However a open command with an tag of
+`tag2` will spawn a new command:
+
+....
+TAG=tag2 /example-bridge --tag tag2
+....
+
+If you need to include `${}`, as an actual value in your arguments you
+can escape it by prefixing it with a `\`
+
+[[package-replace]]
+=== Replacing an existing package
+
+If the functionality in a package replaces that of another package then
+it can replace that package by claiming the same `name` and a higher
+`priority`.
+
+For example, a package in the `/usr/share/cockpit/disks` directory could
+replace Cockpit's _storage_ package with a `manifest.json` like this:
+
+....
+{
+ "version": 0,
+ "name": "storage",
+ "priority": 10,
+ "menu": {
+ "index": {
+ "label": "Disk Storage",
+ "order": 15
+ }
+ }
+}
+....
diff --git a/doc/guide/asciidoc/privileges.adoc b/doc/guide/asciidoc/privileges.adoc
new file mode 100644
index 000000000000..cbc6205cb96a
--- /dev/null
+++ b/doc/guide/asciidoc/privileges.adoc
@@ -0,0 +1,58 @@
+[[privileges]]
+== Privileges and Permissions
+
+When a user is logged into Cockpit, they are logged into a normal
+session that has exactly the same privileges as if they logged in via
+SSH or on the console.
+
+However, Cockpit will usually try to escalate the privileges of the user
+using https://www.freedesktop.org/wiki/Software/polkit/[Policy Kit] or
+https://www.sudo.ws/[sudo]. If the user is able to escalate privileges
+from the command line by typing in their password again (or without
+typing in any password), then Cockpit will be able to escalate the
+privileges of the session to "root" immediately upon login.
+
+The user can change the privileges of a session from within that
+session, via the "Administrative access" indicator in the top bar. From
+that indicator, the user can drop "root" privileges and regain them. On
+the next login, Cockpit will give the session the same privileges.
+
+Usually a user needs to be in the `wheel` Unix user group for the user
+to be able to escalate privileges in this way. However both Policy Kit
+and sudo may be configured to use other criteria.
+
+[[privileges-polkit]]
+=== Customizing Polkit Privileges
+
+Services like
+https://www.freedesktop.org/wiki/Software/systemd/[systemd] and
+https://wiki.gnome.org/Projects/NetworkManager[NetworkManager] use
+https://www.freedesktop.org/wiki/Software/polkit/[Polkit] to validate
+and escalate privileges. It is possible to customize these rules with
+files in `/etc/polkit-1/rules.d`.
+
+Polkit rules files are
+https://www.freedesktop.org/software/polkit/docs/latest/polkit.8.html[javascript
+with specific methods and objects]. For example, placing the following
+polkit rule to `/etc/polkit-1/rules.d/10-operators.rule` allows all
+users in the `operators` group to start, stop, restart and otherwise
+manage systemd services:
+
+....
+polkit.addRule(function(action, subject) {
+ if (action.id == "org.freedesktop.systemd1.manage-units") {
+ if (subject.isInGroup("operators")) {
+ return polkit.Result.YES;
+ }
+ }
+});
+....
+
+In order to allow a certain group to perform any administrative action
+you could add a rule like this:
+
+....
+polkit.addAdminRule(function(action, subject) {
+ return ["unix-group:operators"];
+});
+....
diff --git a/doc/guide/asciidoc/sso.adoc b/doc/guide/asciidoc/sso.adoc
new file mode 100644
index 000000000000..73f145cb1c4b
--- /dev/null
+++ b/doc/guide/asciidoc/sso.adoc
@@ -0,0 +1,179 @@
+[[sso]]
+== Single Sign On
+
+Cockpit can use Kerberos for Single Sign On authentication, where users
+are automatically authenticated if they have a valid Kerberos ticket.
+
+[[sso-server]]
+=== Server Requirements
+
+To authenticate users, the server that Cockpit is running on must be
+joined to a domain. This can usually be accomplished using the
+https://freedesktop.org/software/realmd/docs/realm.html[`realm join example.com`]
+command.
+
+The domain must be resolvable by DNS. For instance, the SRV records of
+the kerberos server should be resolvable:
+
+....
+$ host -t SRV _kerberos._udp.example.com
+_kerberos._udp.example.com has SRV record 0 100 88 dc.example.com
+....
+
+The server running Cockpit should have a fully qualified name that ends
+with the domain name.
+
+There must be a valid Kerberos host key for the server in the
+`/etc/krb5.keytab` file. Alternatively, if you would like to use a
+different keytab, you can do so by placing it in
+`/etc/cockpit/krb5.keytab`, or below `$XDG_CONFIG_DIRS` if set (see
+link:./cockpit.conf.5.html[cockpit.conf]). It may be necessary to create
+a kerberos service principal and update the keytab if it is not present.
+Depending on your domain type different service names are required:
+
+Active Directory::
+ `HOST/server.example.com@EXAMPLE.COM`
+IPA and MIT::
+ `HTTP/server.example.com@EXAMPLE.COM`
+
+When joining an IPA domain with Cockpit and the `ipa` command line tool
+is available, both the service principal name and a
+`/etc/cockpit/krb5.keytab` get created automatically, so that Kerberos
+based single sign on into Cockpit works out of the box. If you want/need
+to do this by hand or in a script, first create or modify the `HTTP/`
+service principal:
+
+....
+$ sudo ipa service-add --ok-as-delegate=true --ok-to-auth-as-delegate=true \
+ HTTP/server.example.com@EXAMPLE.COM
+# or, if it already exists, just enable delegation:
+$ sudo ipa service-mod --ok-as-delegate=true --ok-to-auth-as-delegate=true \
+ HTTP/server.example.com@EXAMPLE.COM
+....
+
+Then generate a key for that principal:
+
+....
+$ sudo ipa-getkeytab -p HTTP/server.example.com@EXAMPLE.COM -k /etc/cockpit/krb5.keytab
+....
+
+The following command can be used to list the
+`/etc/cockpit/krb5.keytab`:
+
+....
+$ sudo klist -k /etc/cockpit/krb5.keytab
+....
+
+Lastly accounts from the domain must be resolvable to unix accounts on
+the server running Cockpit. For example:
+
+....
+$ getent passwd user@example.com
+user@example.com:*:381001109:381000513:User Name:/home/user:/bin/sh
+....
+
+If you wish to delegate your kerberos credentials to Cockpit, and allow
+Cockpit to then connect to other machines using those credentials, you
+should enable delegation for the hosts running Cockpit, and in some
+cases the `HTTP` service as well. When joining an IPA domain, this is
+enabled by default.
+
+Domain admins (usually the `admins@example.com` group) should normally
+also be able to administer any joined machine. Enable sudo access for
+that group with the following command on the IPA server, for version
+4.7.1 and later:
+
+....
+ipa-advise enable-admins-sudo | sh -ex
+....
+
+On earlier FreeIPA versions, run these commands instead, as a domain
+admin on any joined machine:
+
+....
+ipa sudorule-add --hostcat=all --cmdcat=all All
+ipa sudorule-add-user --groups=admins All
+....
+
+Note that this does not change security properties; domain admins can
+give this privilege to themselves, so it is safe to enable by default.
+
+[[sso-client]]
+=== Client Requirements
+
+The client side, where your web browser is running, should have a valid
+kerberos ticket in the current user session. A command like this will
+get one:
+
+....
+$ kinit user@EXAMPLE.COM
+Password for user@EXAMPLE.COM:
+....
+
+In addition your browser must be usually be configured to allow kerberos
+authentication for the domain.
+
+Mozilla Firefox::
+ Go to `about:config` and set the `network.negotiate-auth.trusted-uris`
+ setting to your domain name preceded by a dot, ie: `.example.com`
+Google Chrome::
+ On Linux: create the file
+ `/etc/opt/chrome/policies/managed/example-com.json` with the contents:
+ +
+....
+{
+ "AuthServerWhitelist": "*example.com"
+}
+....
+ +
+ and restart the browser. On other platforms, exit your browser
+ completely, and start it with a command line like this:
+ `google-chrome --auth-server-whitelist=*example.com`
+
+Use a fully qualified server name (with the domain name at the end) to
+access Cockpit in your web browser.
+
+If you wish to connect from one server to another in Cockpit using
+kerberos SSO, then you have to explicitly enable all sorts of things.
+For starters, make sure that delegated credentials are allowed by your
+domain (see above). Next when requesting your kerberos ticket make sure
+that forwardable tickets are requested:
+
+....
+$ kinit -f user@EXAMPLE.COM
+Password for user@EXAMPLE.COM:
+....
+
+Make sure that the forwardable flag `F` is present in your ticket:
+
+....
+$ klist -f
+Ticket cache: KEYRING:persistent:1000:1000
+Default principal: user@EXAMPLE.COM
+
+Valid starting Expires Service principal
+18.03.2017 05:39:23 19.03.2017 05:39:20 krbtgt/EXAMPLE.COM@EXAMPLE.COM
+ Flags: FIA
+....
+
+Lastly configure your browser to allow delegated, forwardable kerberos
+credentials to be sent to Cockpit:
+
+Mozilla Firefox::
+ Go to `about:config` and set the
+ `network.negotiate-auth.delegation-uris` setting to your domain name
+ preceded by a dot, ie: `.example.com`
+Google Chrome::
+ On Linux: create the file
+ `/etc/opt/chrome/policies/managed/example-com.json` with the contents:
+ +
+....
+{
+ "AuthServerWhitelist": "*example.com",
+ "AuthNegotiateDelegateWhitelist": "*example.com"
+}
+....
+ +
+ and restart the browser. On other platforms, exit your browser
+ completely, and start it with a command line like this:
+ `google-chrome --auth-server-whitelist=*example.com --auth-negotiate-delegate-whitelist=*example.com`
diff --git a/doc/guide/asciidoc/startup.adoc b/doc/guide/asciidoc/startup.adoc
new file mode 100644
index 000000000000..3b530952dc37
--- /dev/null
+++ b/doc/guide/asciidoc/startup.adoc
@@ -0,0 +1,41 @@
+[[startup]]
+== Start up
+
+Cockpit's `cockpit-ws` component is what the browser connects to and it
+typically starts on demand via
+https://www.freedesktop.org/wiki/Software/systemd/[`systemd`] socket
+activation.
+
+The actual `cockpit.service` and `cockpit-ws` process will start on
+demand when a browser accesses the `cockpit.socket`,
+link:#listen[usually on port 9090]. Once a user logs in then a
+`cockpit-bridge` process will be started in a Linux user login session.
+
+Only systems that you connect to with your browser need to have the
+`cockpit.socket` enabled. For systems that you add through host switcher
+the bridge is started via SSH on demand.
+
+[[startup-shutdown]]
+=== Process exit
+
+The `cockpit-bridge` process will exit when the user logs out. In
+addition, after 10 minutes of inactivity, the `cockpit-ws` process will
+exit on its own. The browser will automatically disconnect if it fails
+to hear from the `cockpit-ws` process for 30 seconds.
+
+[[startup-boot]]
+=== Boot start up
+
+To make Cockpit available by default after system boot the
+`cockpit.socket` needs to be enabled:
+
+....
+$ sudo systemctl enable cockpit.socket
+....
+
+If you wish to not have Cockpit available by default via a browser, then
+the `cockpit.socket` should be disabled:
+
+....
+$ sudo systemctl disable cockpit.socket
+....
diff --git a/doc/guide/asciidoc/urls.adoc b/doc/guide/asciidoc/urls.adoc
new file mode 100644
index 000000000000..f0639efb449f
--- /dev/null
+++ b/doc/guide/asciidoc/urls.adoc
@@ -0,0 +1,68 @@
+[[urls]]
+== Cockpit URLs
+
+Cockpit URLs follow a specific structure, related to the components they
+are loading. Various components are loaded in `