summaryrefslogtreecommitdiff
path: root/www/server
diff options
context:
space:
mode:
authorcashmere <cashmere@autistici.org>2026-09-04 09:05:24 +0200
committercashmere <cashmere@autistici.org>2026-09-04 09:05:24 +0200
commit4f49b16079b27d7ce633619e4cddfdf2bffbc952 (patch)
treec296c1e7f3c3e2f6859c6d3a1b1b208779b89679 /www/server
downloadrelic-master.tar.gz
initial commitHEADmaster
Diffstat (limited to 'www/server')
-rw-r--r--www/server/any/althttpd.md42
-rw-r--r--www/server/any/cgi.md117
-rw-r--r--www/server/any/http-over-ssh.md173
-rw-r--r--www/server/any/index.md11
-rw-r--r--www/server/any/inetd.md52
-rw-r--r--www/server/any/none.md56
-rw-r--r--www/server/any/scgi.md72
-rw-r--r--www/server/any/stunnel.md81
-rw-r--r--www/server/any/xinetd.md27
-rw-r--r--www/server/debian/index.md6
-rw-r--r--www/server/debian/nginx.md395
-rw-r--r--www/server/debian/service.md259
-rw-r--r--www/server/index.html316
-rw-r--r--www/server/macos/index.md5
-rw-r--r--www/server/macos/service.md181
-rw-r--r--www/server/openbsd/fastcgi.md302
-rw-r--r--www/server/openbsd/index.md6
-rw-r--r--www/server/openbsd/service.wiki118
-rw-r--r--www/server/whyuseaserver.wiki95
-rw-r--r--www/server/windows/cgi-bin-perm.pngbin0 -> 15784 bytes
-rw-r--r--www/server/windows/cgi-exec-perm.pngbin0 -> 6789 bytes
-rw-r--r--www/server/windows/cgi-install-iis.pngbin0 -> 34422 bytes
-rw-r--r--www/server/windows/cgi-script-map.pngbin0 -> 7456 bytes
-rw-r--r--www/server/windows/cgi.md115
-rw-r--r--www/server/windows/iis.md134
-rw-r--r--www/server/windows/index.md8
-rw-r--r--www/server/windows/none.md69
-rw-r--r--www/server/windows/service.md120
-rw-r--r--www/server/windows/stunnel.md156
29 files changed, 2916 insertions, 0 deletions
diff --git a/www/server/any/althttpd.md b/www/server/any/althttpd.md
new file mode 100644
index 0000000..7797103
--- /dev/null
+++ b/www/server/any/althttpd.md
@@ -0,0 +1,42 @@
+# Serving via althttpd
+
+[Althttpd][althttpd]
+is a light-weight web server that has been used to implement the SQLite and
+Fossil websites for well over a decade. Althttpd strives for simplicity,
+security, ease of configuration, and low resource usage.
+
+To set up a Fossil server as CGI on a host running the althttpd web
+server, follow these steps.
+<ol>
+<li>Get the althttpd webserver running on the host. This is easily
+done by following the [althttpd documentation][althttpd].
+
+<li>Create a CGI script for your Fossil repository. The script will
+be typically be two lines of code that look something like this:
+
+~~~
+ #!/usr/bin/fossil
+ repository: /home/yourlogin/fossils/project.fossil
+~~~
+
+Modify the filenames to conform to your system, of course. The
+CGI script accepts [other options][cgi] besides the
+repository:" line. You can add in other options as you desire,
+but the single "repository:" line is normally all that is needed
+to get started.
+
+<li>Make the CGI script executable.
+
+<li>Verify that the fossil repository file and the directory that contains
+the repository are both writable by whatever user the web server is
+running and.
+</ol>
+
+And you are done. Visit the URL that corresponds to the CGI script
+you created to start using your Fossil server.
+
+*[Return to the top-level Fossil server article.](../)*
+
+
+[althttpd]: https://sqlite.org/althttpd/
+[cgi]: ../../cgi.wiki
diff --git a/www/server/any/cgi.md b/www/server/any/cgi.md
new file mode 100644
index 0000000..79ede92
--- /dev/null
+++ b/www/server/any/cgi.md
@@ -0,0 +1,117 @@
+# Serving via CGI
+
+A Fossil server can be run from most ordinary web servers as a CGI
+program. This feature allows Fossil to seamlessly integrate into a
+larger website. The [self-hosting Fossil repository web
+site](../../selfhost.wiki) is implemented using CGI. See the
+[How CGI Works](../../aboutcgi.wiki) page for background information
+on the CGI protocol.
+
+To run Fossil as CGI, create a CGI script (here called "repo") in the
+CGI directory of your web server with content like this:
+
+ #!/usr/bin/fossil
+ repository: /home/fossil/repo.fossil
+
+Adjust the paths appropriately. It may be necessary to set certain
+permissions on this file or to modify an `.htaccess` file or make other
+server-specific changes. Consult the documentation for your particular
+web server. The following permissions are *normally* required, but,
+again, may be different for a particular configuration:
+
+* The Fossil binary (`/usr/bin/fossil` in the example above)
+ must be readable/executable.
+
+* *All* directories leading up to the Fossil binary must be readable
+ by the process which executes the CGI.
+
+* The CGI script must be executable for the user under which it will
+ run, which often differs from the one running the web server.
+ Consult your site's documentation or the web server’s system
+ administrator.
+
+* *All* directories leading to the CGI script must be readable by the
+ web server.
+
+* The repository file *and* the directory containing it must be
+ writable by the same account which executes the Fossil binary.
+ (This might differ from the user the web server normally runs
+ under.) The directory holding the repository file(s) needs to be
+ writable so that SQLite can write its journal files. When using
+ another access control system, such as AppArmor or SELinux, it may
+ be necessary to explicitly permit that account to read and write
+ the necessary files. Also verify a possible _systemd_ sandboxing of
+ the web server service, especially the combination of _ProtectSystem_,
+ _ProtectHome_, and _ReadWriteDirectories_/_ReadWritePaths_.
+
+* Fossil must be able to create temporary files in a
+ [directory that varies by host OS](../../env-opts.md#temp). When the
+ CGI process is operating [within a chroot](../../chroot.md),
+ ensure that this directory exists and is readable/writeable by the
+ user who executes the Fossil binary.
+
+Once the CGI script is set up correctly, and assuming your server is
+also set correctly, you should be able to access your repository with a
+URL like: <b>http://mydomain.org/cgi-bin/repo</b> This is assuming you
+are running a web server like Apache that uses a “`cgi-bin`” directory
+for scripts like our “`repo`” example.
+
+To serve multiple repositories from a directory using CGI, use the
+"directory:" tag in the CGI script rather than "repository:". You
+might also want to add a "notfound:" tag to tell where to redirect if
+the particular repository requested by the URL is not found:
+
+ #!/usr/bin/fossil
+ directory: /home/fossil/repos
+ notfound: http://url-to-go-to-if-repo-not-found/
+
+Once deployed, a URL like: <b>http://mydomain.org/cgi-bin/repo/XYZ</b>
+will serve up the repository `/home/fossil/repos/XYZ.fossil` if it
+exists.
+
+Additional options available to the CGI script are [documented
+separately](../../cgi.wiki).
+
+#### CGI with Apache behind an Nginx proxy
+
+For the case where the Fossil repositories live on a computer, itself behind
+an Internet-facing machine that employs Nginx to reverse proxy HTTP(S) requests
+and take care of the TLS part of the connections in a transparent manner for
+the downstream web servers, the CGI parameter `HTTPS=on` might not be set.
+However, Fossil in CGI mode needs it in order to generate the correct links.
+
+Apache can be instructed to pass this parameter further to the CGI scripts for
+TLS connections with a stanza like
+
+ SetEnvIf X-Forwarded-Proto "https" HTTPS=on
+
+in its config file section for CGI, provided that
+
+ proxy_set_header X-Forwarded-Proto $scheme;
+
+has been be added in the relevant proxying section of the Nginx config file.
+
+*[Return to the top-level Fossil server article.](../)*
+
+#### Apache mod_cgi and `CONTENT_LENGTH`
+
+At some point in its 2.4.x family, Apache's `mod_cgi` stopped relaying
+the Content-Length header in the HTTP reply from CGIs back to clients.
+However, Fossil clients prior to 2024-04-17 depended on seeing the
+Content-Length header and were unable to handle HTTP replies without
+one. The change in Apache behavior caused "fossil clone" and "fossil
+sync" to stop working. There are two possible fixes to this problem:
+
+ 1. Restore legacy behavior in Apache by adding
+ the following to the Apache configuration, scoped to the `<Directory>`
+ entry or entries in which fossil is being served via CGI:
+ <blockquote><pre>
+ &lt;Directory ...&gt;
+ SetEnv ap_trust_cgilike_cl "yes"
+ &lt;/Directory&gt;
+ </pre></blockquote>
+
+ 2. Upgrade your Fossil client to any trunk check-in after 2024-04-17,
+ as Fossil was upgraded to be able to deal with the missing
+ Content-Length field by
+ [check-in a8e33fb161f45b65](/info/a8e33fb161f45b65).
diff --git a/www/server/any/http-over-ssh.md b/www/server/any/http-over-ssh.md
new file mode 100644
index 0000000..8bc6fdb
--- /dev/null
+++ b/www/server/any/http-over-ssh.md
@@ -0,0 +1,173 @@
+# Forcing Use of Fossil’s RBAC over SSH
+
+Andy Bradford posted a [clever solution][sshfc] to the problem of
+Fossil’s RBAC system [being ignored](../../caps/#webonly) over `ssh://`
+URLs: use OpenSSH’s `ForceCommand` feature to route the sync transfer
+protocol data over `fossil http` rather than `fossil test-http`.
+
+The setup for this is complicated, but it’s a worthy option when you
+need encrypted communications between the client and server, you already
+have SSH set up, and [the HTTPS alternative](../../ssl.wiki) is
+unworkable for some reason.
+
+
+## 1. Force remote Fossil access through a wrapper script <a id="sshd"></a>
+
+Put something like the following into the `sshd_config` file on the
+Fossil repository server:
+
+``` ssh-config
+Match Group fossil
+ X11Forwarding no
+ AllowTcpForwarding no
+ AllowAgentForwarding no
+ ForceCommand /home/fossil/bin/wrapper
+```
+
+This file is usually found in `/etc/ssh`, but some OSes put it
+elsewhere.
+
+The first line presumes that we will put all users who need to use our
+Fossil repositories into the `fossil` group, as we will do
+[below](#perms). You could instead say something like:
+
+``` ssh-config
+Match User alice,bob,carol,dave
+```
+
+You have to list the users allowed to use Fossil in this case because
+your system likely has a system administrator that uses SSH for remote
+shell access, so you want to *exclude* that user from the list. For the
+same reason, you don’t want to put the `ForceCommand` directive outside
+a `Match` block of some sort.
+
+You could instead list the exceptions:
+
+``` ssh-config
+Match User !evi
+```
+
+This would permit only [Evi the System Administrator][evi] to bypass this
+mechanism.
+
+[evi]: https://en.wikipedia.org/wiki/Evi_Nemeth
+
+If you have a user that needs both interactive SSH shell access *and*
+Fossil access, exclude that user from the `Match` rule and use Fossil’s
+normal `ssh://` URL scheme for those cases. This user will bypass the
+Fossil RBAC, but they effectively have Setup capability on those
+repositories anyway by having full read/write access to the DB files via
+the shell.
+
+
+## 2. Rewrite the sync command with that wrapper <a id="wrapper"></a>
+
+When Fossil syncs over SSH, it attempts to launch a remote Fossil
+instance with certain parameters in order to set up the HTTP-based sync
+protocol over that SSH tunnel. We need to preserve some of this command
+and rewrite other parts to make this work.
+
+Here is a simpler variant of Andy’s original wrapper script:
+
+``` sh
+#!/bin/bash
+set -- $SSH_ORIGINAL_COMMAND
+while [ $# -gt 1 ] ; do shift ; done
+export REMOTE_USER="$USER"
+ROOT=/home/fossil
+exec "$ROOT/bin/fossil" http "$ROOT/museum/$(/bin/basename "$1")"
+```
+
+The substantive changes are:
+
+1. Move the command rewriting bits to the start.
+
+2. Be explicit about executable paths. You might extend this idea by
+ using chroot, BSD jails, Linux containers, etc.
+
+3. Restrict the Fossil repositories to a single flat subdirectory under
+ the `fossil` user’s home directory. This scheme is easier to secure
+ than one allowing subdirectories, since you’d need to take care of
+ `../` and such to prevent a sandbox escape.
+
+4. Don’t take the user name via the SSH command; to this author’s mind,
+ the user should not get to override their Fossil user name on the
+ remote server, as that permits impersonation. The identity you
+ present to the SSH server must be the same identity that the Fossil
+ repository you’re working with knows you by. Since the users
+ selected by “`Match`” block above are dedicated to using only Fossil
+ in this setup, this restriction shouldn’t present a practical problem.
+
+The script’s shebang line assumes `/bin/sh` is POSIX-compliant, but that
+is not the case everywhere. If the script fails to run on your system,
+try changing this line to point at `bash`, `dash`, `ksh`, or `zsh`. Also
+check the absolute paths for local correctness: is `/bin/basename`
+installed on your system, for example?
+
+Under this scheme, you clone with a command like:
+
+ $ fossil clone ssh://HOST/repo.fossil
+
+This will clone the remote `/home/fossil/museum/repo.fossil` repository
+to your local machine under the same name and open it into a “`repo/`”
+subdirectory. Notice that we didn’t have to give the `museum/` part of
+the path: it’s implicit per point #3 above.
+
+This presumes your local user name matches the remote user name. Unlike
+with `http[s]://` URLs, you don’t have to provide the `USER@` part to
+get authenticated access
+since this scheme doesn’t permit anonymous cloning. Only
+if these two user names are different do you need to add the `USER@` bit to the
+URL.
+
+
+## 3. Set permissions <a id="perms"></a>
+
+This scheme assumes that the users covered by the `Match` rule can read
+the wrapper script from where you placed it and execute it, and that
+they have read/write access on the directory where the Fossil
+repositories are stored.
+
+You can achieve all of this on a Linux box with:
+
+``` shell
+sudo adduser fossil
+for u in alice bob carol dave ; do
+ sudo adduser $u
+ sudo gpasswd -a fossil $u
+done
+sudo -i -u fossil
+chmod 710 .
+mkdir -m 750 bin
+mkdir -m 770 museum
+ln -s /usr/local/bin/fossil bin
+```
+
+You then need to copy the Fossil repositories into `~fossil/museum` and
+make them readable and writable by group `fossil`. These repositories
+presumably already have Fossil users configured, with the necessary
+[user capabilities](../../caps/), the point of this article being to
+show you how to make Fossil-over-SSH pay attention to those caps.
+
+You must also permit use of `REMOTE_USER` on each shared repository.
+Fossil only pays attention to this environment variable in certain
+contexts, of which “`fossil http`” is not one. Run this command against
+each repo to allow that:
+
+``` shell
+echo "INSERT OR REPLACE INTO config VALUES ('remote_user_ok',1,strftime('%s','now'));" |
+fossil sql -R museum/repo.fossil
+```
+
+Now you can configure SSH authentication for each user. Since Fossil’s
+password-saving feature doesn’t work in this case, I suggest setting up
+SSH keys via `~USER/.ssh/authorized_keys` since the SSH authentication
+occurs on each sync, which Fossil’s default-enabled autosync setting
+makes frequent.
+
+Equivalent commands for other OSes should be readily discerned from the
+script above.
+
+[sshfc]: forum:/forumpost/0d7d6c3df41fcdfd
+
+<div style="height:50em" id="this-space-intentionally-left-blank"></div>
diff --git a/www/server/any/index.md b/www/server/any/index.md
new file mode 100644
index 0000000..ccf2d94
--- /dev/null
+++ b/www/server/any/index.md
@@ -0,0 +1,11 @@
+# Portable Fossil Service Options
+
+- [Fossil as a standalone HTTP server](./none.md)
+- [SCGI under your web server of choice](./scgi.md)
+- [CGI under your web server of choice](./cgi.md)
+- [CGI under `althttpd`](./althttpd.md)
+- [Behind `stunnel` to get TLS encryption](./stunnel.md)
+- [`inetd`](./inetd.md)
+- [`xinetd`](./xinetd.md)
+
+*[Return to the top-level Fossil server article.](../)*
diff --git a/www/server/any/inetd.md b/www/server/any/inetd.md
new file mode 100644
index 0000000..35766c7
--- /dev/null
+++ b/www/server/any/inetd.md
@@ -0,0 +1,52 @@
+# Serving via inetd
+
+A Fossil server can be launched on-demand by `inetd` by using the
+[`fossil http`](/help/http) command. To do so, add a line like the
+following to its configuration file, typically `/etc/inetd.conf`:
+
+ 80 stream tcp nowait.1000 root /usr/bin/fossil /usr/bin/fossil http /home/fossil/repo.fossil
+
+In this example, you are telling `inetd` that when an incoming
+connection appears on TCP port 80 that it should launch the program
+`/usr/bin/fossil` with the arguments shown. Obviously you will need to
+modify the pathnames for your particular setup. The final argument is
+either the name of the fossil repository to be served or a directory
+containing multiple repositories.
+
+If you use a non-standard TCP port on systems where the port
+specification must be a symbolic name and cannot be numeric, add the
+desired name and port to `/etc/services`. For example, if you want your
+Fossil server running on TCP port 12345 instead of 80, you will need to
+add:
+
+ fossil 12345/tcp # fossil server
+
+and use the symbolic name “`fossil`” instead of the numeric TCP port
+number (“12345” in the above example) in `inetd.conf`.
+
+Notice that we configured `inetd` to launch Fossil as root. See the
+top-level section on “[The Fossil Chroot
+Jail](../../chroot.md)” for the consequences of this and
+alternatives to it.
+
+You can instead configure `inetd` to bind to a higher-numbered TCP port,
+allowing Fossil to be run as a normal user. In that case, Fossil will
+not put itself into a chroot jail, because it assumes you have set up
+file permissions and such on the server appropriate for that user.
+
+The `inetd` daemon must be enabled for this to work, and it must be
+restarted whenever its configuration file changes.
+
+This is a more complicated method than the [standalone HTTP server
+method](./none.md), but it has the advantage of only using system
+resources when an actual connection is attempted. If no one ever
+connects to that port, a Fossil server will not (automatically) run. It
+has the disadvantage of requiring "root" access, which may not be
+available to you, either due to local IT policy or because of
+restrictions at your shared Internet hosting service.
+
+For further details, see the relevant section in your system's
+documentation. The FreeBSD Handbook covers `inetd` in [this
+chapter](https://www.freebsd.org/doc/en/books/handbook/network-inetd.html).
+
+*[Return to the top-level Fossil server article.](../)*
diff --git a/www/server/any/none.md b/www/server/any/none.md
new file mode 100644
index 0000000..2208091
--- /dev/null
+++ b/www/server/any/none.md
@@ -0,0 +1,56 @@
+# Standalone HTTP Server
+
+The easiest way to set up a Fossil server is to use either the
+[`server`](/help/server) or [`ui`](/help/ui) command:
+
+* **fossil server** _REPOSITORY_
+* **fossil ui** _REPOSITORY_
+
+The _REPOSITORY_ argument is either the name of the repository file or a
+directory containing many repositories named “`*.fossil`”. Both of these
+commands start a Fossil server, usually on TCP port 8080, though a
+higher numbered port will be used instead if 8080 is already occupied.
+
+You can access these using URLs of the form **http://localhost:8080/**,
+or if _REPOSITORY_ is a directory, URLs of the form
+**http://localhost:8080/**_repo_**/** where _repo_ is the base name of
+the repository file without the “`.fossil`” suffix.
+
+There are several key differences between “`ui`” and “`server`”:
+
+* “`ui`” always binds the server to the loopback IP address (127.0.0.1)
+ so that it cannot serve to other machines.
+
+* Anyone who visits this URL is treated as the all-powerful Setup
+ user, which is why the first difference exists.
+
+* “`ui`” launches a local web browser pointed at this URL.
+
+You can omit the _REPOSITORY_ argument if you run one of the above
+commands from within a Fossil checkout directory to serve that
+repository:
+
+ $ fossil ui # or...
+ $ fossil server
+
+You can abbreviate Fossil sub-commands as long as they are unambiguous.
+“`server`” can currently be as short as “`ser`”.
+
+You can serve a directory containing multiple `*.fossil` files like so:
+
+ $ fossil server --port 9000 --repolist /path/to/repo/dir
+
+As a security precaution, when the “`server`” or
+“`ui`” commands are run as `root` Fossil will drop into
+a [chroot](../../chroot.md) with the directory where the repository is located
+as the new root of the filesystem.
+
+There is an [example script](/file/tools/fslsrv) in the Fossil
+distribution that wraps `fossil server` to produce more complicated
+effects. Feel free to take it, study it, and modify it to suit your
+local needs.
+
+See the [online documentation](/help/server) for more information on the
+options and arguments you can give to these commands.
+
+*[Return to the top-level Fossil server article.](../)*
diff --git a/www/server/any/scgi.md b/www/server/any/scgi.md
new file mode 100644
index 0000000..4c02158
--- /dev/null
+++ b/www/server/any/scgi.md
@@ -0,0 +1,72 @@
+# Serving via SCGI
+
+There is an alternative to running Fossil as a [standalone HTTP
+server](./none.md), which is to run it in SimpleCGI (a.k.a. SCGI) mode,
+which uses the same [`fossil server`](/help/server) command as for HTTP
+service. Simply add the `--scgi` command-line option and the stand-alone
+server will speak the SCGI protocol rather than raw HTTP.
+
+This can be used with a web server such as [nginx](http://nginx.org)
+which does not support [Fossil’s CGI mode](./cgi.md).
+
+A basic nginx configuration to support SCGI with Fossil looks like this:
+
+ location /code/ {
+ include scgi_params;
+ scgi_param SCRIPT_NAME "/code";
+ scgi_pass localhost:9000;
+ }
+
+The `scgi_params` file comes with nginx, and it simply translates nginx
+internal variables to `scgi_param` directives to create SCGI environment
+variables for the proxied program; in this case, Fossil. Our explicit
+`scgi_param` call to define `SCRIPT_NAME` adds one more variable to this
+set, which is necessary for this configuration to work properly, because
+our repo isn’t at the root of the URL hierarchy. Without it, when Fossil
+generates absolute URLs, they’ll be missing the `/code` part at the
+start, which will typically cause [404 errors][404].
+
+The final directive simply tells nginx to proxy all calls to URLs under
+`/code` down to an SCGI program on TCP port 9000. We can temporarily
+set Fossil up as a server on that port like so:
+
+ $ fossil server /path/to/repo.fossil --scgi --localhost --port 9000 &
+
+The `--scgi` option switches Fossil into SCGI mode from its default,
+which is [stand-alone HTTP server mode](./none.md). All of the other
+options discussed in that linked document — such as the ability to serve
+a directory full of Fossil repositories rather than just a single
+repository — work the same way in SCGI mode.
+
+The `--localhost` option is simply good security: we’re using nginx to
+expose Fossil service to the outside world, so there is no good reason
+to allow outsiders to contact this Fossil SCGI server directly.
+
+Giving an explicit non-default TCP port number via `--port` is a good
+idea to avoid conflicts with use of Fossil’s default TCP service port,
+8080, which may conflict with local uses of `fossil ui` and such.
+
+We characterized the SCGI service start command above as “temporary”
+because running Fossil in the background like that means it won’t start
+back up on a reboot of the server. A simple solution to that is to add
+that command to `/etc/rc.local` on systems that have it. However, you
+might want to consider setting Fossil up as an OS service instead, so
+that you get the benefits of the platform’s service management
+framework:
+
+* [Linux (systemd)](../debian/service.md)
+* [Windows service](../windows/service.md)
+* [macOS (launchd)](../macos/service.md)
+* [xinetd](../any/xinetd.md)
+* [inetd](../any/inetd.md)
+
+We go into more detail on nginx service setup with Fossil in our
+[Debian/Ubuntu specific guide](../debian/nginx.md), which also
+gets you TLS service.
+
+Similarly, our [OpenBSD specific guide](../openbsd/fastcgi.md) details how
+to setup a Fossil server using httpd and FastCGI on OpenBSD.
+
+*[Return to the top-level Fossil server article.](../)*
+
+[404]: https://en.wikipedia.org/wiki/HTTP_404
diff --git a/www/server/any/stunnel.md b/www/server/any/stunnel.md
new file mode 100644
index 0000000..c8824a7
--- /dev/null
+++ b/www/server/any/stunnel.md
@@ -0,0 +1,81 @@
+# Serving via stunnel
+
+[`stunnel`](https://www.stunnel.org/) is a TLS/SSL proxy for programs
+that themselves serve only via HTTP, such as Fossil. (Fossil *can* speak
+HTTPS, but only as a client.) `stunnel` decodes the HTTPS data from the
+outside world as HTTP before passing it to Fossil, and it encodes the
+HTTP replies from Fossil as HTTPS before sending them to the remote host
+that made the request.
+
+You can run `stunnel` in one of two modes: socket listener — much like
+in our [`inetd` doc](./inetd.md) — and as an HTTP reverse proxy. We’ll
+cover both cases here, separately.
+
+
+## <a id="sa"></a>Socket Activation
+
+The following `stunnel.conf` configuration configures it to run Fossil
+in socket listener mode, launching Fossil only when an HTTPS hit comes
+in, then shutting it back down as soon as the transaction is complete:
+
+```dosini
+ [fossil]
+ accept = 443
+ TIMEOUTclose = 0
+ exec = /usr/bin/fossil
+ execargs = /usr/bin/fossil http /home/fossil/ubercool.fossil --https
+ cert = /etc/letsencrypt/live/ubercool-project.org/fullchain.pem
+ key = /etc/letsencrypt/live/ubercool-project.org/privkey.pem
+ ciphers = ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES128-SHA:DES-CBC3-SHA
+ options = CIPHER_SERVER_PREFERENCE
+```
+
+This configuration shows the TLS certificate generated by the [Let’s
+Encrypt](https://letsencrypt.org) [Certbot](https://certbot.eff.org) in
+[certonly mode](https://certbot.eff.org/lets-encrypt/debianbuster-other).
+There are other ways to get TLS certificates, but this is a popular and
+free option.
+
+You will need to adjust the site names and paths in this example. Where
+this file goes varies by OS type, so check the man pages on your system
+to find out where it should be locally.
+
+See the `stunnel` documentation for further details about this
+configuration file.
+
+It is important that the [`fossil http`](/help/http) command in that
+configuration include the `--https` option to let Fossil know to use
+“`https://`” instead of “`http://`” in generated hyperlinks.
+
+
+
+## <a id="proxy"></a>Reverse Proxy
+
+You can instead have Fossil running in the background in [standalone
+HTTP server mode](./none.md), bound to a high random TCP port number on
+localhost via the `--localhost` and `--port` flags, then configure
+`stunnel` to reverse proxy public HTTPS connections down to it via HTTP.
+
+The configuration is the same as the above except that you drop the
+`exec` and `execargs` directives and add this instead:
+
+```dosini
+ connect = 9000
+```
+
+That tells `stunnel` to connect to an already-running process listening
+on the given TCP port number.
+
+There are a few advantages to this mode:
+
+1. At the cost of some server memory and a tiny bit of idle CPU time,
+ Fossil remains running so that hits can be served a smidge faster
+ than in socket listener mode, where the Fossil binary has to be
+ loaded and re-initialized on each HTTPS hit.
+
+2. The socket listener mode doesn’t work on all platforms that
+ `stunnel` runs on, particularly [on Windows](../windows/stunnel.md).
+
+*[Return to the top-level Fossil server article.](../)*
+
+<div style="height:50em" id="this-space-intentionally-left-blank"></div>
diff --git a/www/server/any/xinetd.md b/www/server/any/xinetd.md
new file mode 100644
index 0000000..9e93502
--- /dev/null
+++ b/www/server/any/xinetd.md
@@ -0,0 +1,27 @@
+# Serving via xinetd
+
+Some operating systems have replaced the old Unix `inetd` daemon with
+`xinetd`, which has a similar mission but with a very different
+configuration file format.
+
+The typical configuration file is either `/etc/xinetd.conf` or a subfile
+in the `/etc/xinetd.d` directory. You need a configuration something
+like this for Fossil:
+
+ service http
+ {
+ port = 80
+ socket_type = stream
+ wait = no
+ user = root
+ server = /usr/bin/fossil
+ server_args = http /home/fossil/repos/
+ }
+
+This example configures Fossil to serve multiple repositories under the
+`/home/fossil/repos/` directory.
+
+Beyond this, see the general commentary in our article on [the `inetd`
+method](./inetd.md) as they also apply to service via `xinetd`.
+
+*[Return to the top-level Fossil server article.](../)*
diff --git a/www/server/debian/index.md b/www/server/debian/index.md
new file mode 100644
index 0000000..220d177
--- /dev/null
+++ b/www/server/debian/index.md
@@ -0,0 +1,6 @@
+# Debian/Ubuntu Specific Fossil Service Options
+
+- [SCGI under nginx](./nginx.md)
+- [`systemd`](./service.md)
+
+*[Return to the top-level Fossil server article.](../)*
diff --git a/www/server/debian/nginx.md b/www/server/debian/nginx.md
new file mode 100644
index 0000000..0140b37
--- /dev/null
+++ b/www/server/debian/nginx.md
@@ -0,0 +1,395 @@
+# Serving via nginx on Debian and Ubuntu
+
+This document is an extension of [the platform-independent SCGI
+instructions][scgii], which may suffice for your purposes if your needs
+are simple.
+
+Here, we add more detailed information on nginx itself, plus details
+about running it on Debian type OSes. This document was originally
+written for and tested on Debian 10 (Buster) and Ubuntu 20.04, which
+were common Tier 1 OS offerings for [virtual private servers][vps]
+at the time. The same configuration appears to run on Ubuntu 22.04
+LTS without change. This material may not work for older OSes. It is
+known in particular to not work as given for Debian 9 and older!
+
+We also cover [adding TLS](#tls) to the basic configuration, because several
+details depend on the host OS and web stack details. Besides, TLS is
+widely considered part of the baseline configuration these days.
+
+[scgii]: ../any/scgi.md
+[vps]: https://en.wikipedia.org/wiki/Virtual_private_server
+
+
+## <a id="benefits"></a>Benefits
+
+This scheme is considerably more complicated than the [standalone HTTP
+server](../any/none.md) and [CGI options](../any/cgi.md). Even with the
+benefit of this guide and pre-built binary packages, it requires quite a
+bit of work to set it up. Why should you put up with this complexity?
+Because it gives many benefits that are difficult or impossible to get
+with the less complicated options:
+
+* **Power** — nginx is one of the most powerful web servers in the
+ world. The chance that you will run into a web serving wall that you
+ can’t scale with nginx is very low.
+
+ To give you some idea of the sort of thing you can readily
+ accomplish with nginx, your author runs a single public web server
+ that provides transparent name-based virtual hosting for four
+ separate domains:
+
+ * <p>One is entirely static, not involving any dynamic content or
+ Fossil integration at all.</p>
+
+ * <p>Another is served almost entirely by Fossil, with a few select
+ static content exceptions punched past Fossil, which are handled
+ entirely via nginx.</p>
+
+ * <p>The other two domains are aliases for one another — e.g.
+ `example.com` and `example.net` — with most of the content being
+ static. This pair of domains has several unrelated Fossil repo
+ proxies attached to various sections of the URI hierarchy.</p>
+
+ By using nginx, I was able to do all of the above with minimal
+ repetition between the site configurations.
+
+* **Integration** — Because nginx is so popular, it integrates with
+many different technologies, and many other systems integrate with it in
+turn. This makes it great middleware, sitting between the outer web
+world and interior site services like Fossil. It allows Fossil to
+participate seamlessly as part of a larger web stack.
+
+* **Availability** — nginx is already in most operating system binary
+package repositories, so you don’t need to go out of your way to get it.
+
+
+## <a id="modes"></a>Fossil Service Modes
+
+Fossil provides four major ways to access a repository it’s serving
+remotely, three of which are straightforward to use with nginx:
+
+* **HTTP** — Fossil has [a built-in HTTP server](../any/none.md).
+ While this method is efficient and it’s
+ possible to use nginx to proxy access to another HTTP server, we
+ don’t see any particularly good reason to make nginx reinterpret
+ Fossil’s own implementation of HTTP when we have a better option.
+ (But see [below](#http).)
+
+* **SSH** — This method exists primarily to avoid the need for HTTPS,
+ but we *want* HTTPS. (We’ll get to that [below](#tls).)
+ There is probably a way to get nginx to proxy Fossil to HTTPS via
+ SSH, but it would be pointlessly complicated.
+
+* **CGI** — This method is simple but inefficient, because it launches
+ a separate Fossil instance on every HTTP hit.
+ Since Fossil is a relatively small self-contained program, and it’s
+ designed to start up quickly, this method can work well in a
+ surprisingly large number of cases.
+ Nevertheless, we will avoid this option in this document because
+ we’re already buying into a certain amount of complexity here in
+ order to gain power. There’s no sense in throwing away any of that
+ hard-won performance on CGI overhead.
+
+* **SCGI** — The [SCGI protocol][scgip] provides the simplicity of CGI
+ without its performance problems.
+
+SCGI it is, then.
+
+[scgip]: https://en.wikipedia.org/wiki/Simple_Common_Gateway_Interface
+
+
+## <a id="deps"></a>Installing the Dependencies
+
+The first step is to install some non-default packages we’ll need. SSH into
+your server, then say:
+
+ $ sudo apt install fossil nginx
+
+You can leave “`fossil`” out of that if you’re building Fossil from
+source to get a more up-to-date version than is shipped with the host
+OS.
+
+
+## <a id="scgi"></a>Running Fossil in SCGI Mode
+
+For the following nginx configuration to work, it needs to contact a
+background Fossil instance speaking the SCGI protocol. There are
+[many ways](../) to set that up, such as [with `systemd`](./service.md)
+on mainstream Linux distros. Another way is to [containerize][ctz] your
+repository servers, then use the [`fslsrv` wrapper for Podman][fspm] to
+generate `systemd` units for use by the front-end proxy.
+
+However you do it, you need to match up the TCP port numbers between it
+and those in the nginx configuration below.
+
+[ctz]: ../../containers.md
+[fspm]: https://tangentsoft.com/fossil/dir/bin
+
+
+## <a id="config"></a>Configuration
+
+On Debian and Ubuntu systems the primary user-level configuration file
+for nginx is `/etc/nginx/sites-enabled/default`. I recommend that this
+file contain only a list of include statements, one for each site that
+server hosts:
+
+ include local/example.com
+ include local/foo.net
+
+Those files then each define one domain’s configuration. Here,
+`/etc/nginx/local/example.com` contains the configuration for
+`*.example.com` and its alias `*.example.net`; and `local/foo.net`
+contains the configuration for `*.foo.net`.
+
+The configuration for our `example.com` web site, stored in
+`/etc/nginx/sites-enabled/local/example.com` is:
+
+----
+
+ server {
+ server_name .example.com .example.net "";
+ include local/generic;
+ include local/code;
+
+ access_log /var/log/nginx/example.com-https-access.log;
+ error_log /var/log/nginx/example.com-https-error.log;
+
+ # Bypass Fossil for the static documentation generated from
+ # our source code by Doxygen, so it merges into the embedded
+ # doc URL hierarchy at Fossil’s $ROOT/doc without requiring that
+ # these generated files actually be stored in the repo. This
+ # also lets us set aggressive caching on these docs, since
+ # they rarely change.
+ location /code/doc/html {
+ root /var/www/example.com/code/doc/html;
+
+ location ~* \.(html|ico|css|js|gif|jpg|png)$ {
+ add_header Vary Accept-Encoding;
+ access_log off;
+ expires 7d;
+ }
+ }
+
+ # Redirect everything under /code to the Fossil instance
+ location /code {
+ include local/code;
+
+ # Extended caching for URLs that include unique IDs
+ location ~ "/(artifact|doc|file|raw)/[0-9a-f]{40,64}" {
+ add_header Cache-Control "public, max-age=31536000, immutable";
+ include local/code;
+ access_log off;
+ }
+
+ # Lesser caching for URLs likely to be quasi-static
+ location ~* \.(css|gif|ico|js|jpg|png)$ {
+ add_header Vary Accept-Encoding;
+ include local/code;
+ access_log off;
+ expires 7d;
+ }
+ }
+ }
+
+----
+
+As you can see, this is a pure extension of [the basic nginx service
+configuration for SCGI][scgii], showing off a few ideas you might want to
+try on your own site, such as static asset proxying.
+
+You also need a `local/code` file containing:
+
+ include scgi_params;
+ scgi_pass 127.0.0.1:12345;
+ scgi_param SCRIPT_NAME "/code";
+
+We separate that out because nginx refuses to inherit certain settings
+between nested location blocks, so rather than repeat them, we extract
+them to this separate file and include it from both locations where it’s
+needed. You see this above where we set far-future expiration dates on
+files served by Fossil via URLs that contain hashes that change when the
+content changes. It tells your browser that the content of these URLs
+can never change without the URL itself changing, which makes your
+Fossil-based site considerably faster.
+
+Similarly, the `local/generic` file referenced above helps us reduce unnecessary
+repetition among the multiple sites this configuration hosts:
+
+ root /var/www/$host;
+
+ listen 80;
+ listen [::]:80;
+
+ charset utf-8;
+
+There are some configuration directives that nginx refuses to substitute
+variables into, citing performance considerations, so there is a limit
+to how much repetition you can squeeze out this way. One such example
+are the `access_log` and `error_log` directives, which follow an obvious
+pattern from one host to the next. Sadly, you must tolerate some
+repetition across `server { }` blocks when setting up multiple domains
+on a single server.
+
+The configuration for `foo.net` is similar.
+
+See [the nginx docs](https://nginx.org/en/docs/) for more ideas.
+
+
+## <a id="http"></a>Proxying HTTP Anyway
+
+[Above](#modes), we argued that proxying SCGI is a better option than
+making nginx reinterpret Fossil’s own implementation of HTTP. If you
+want Fossil to speak HTTP, just [set Fossil up as a standalone
+server](../any/none.md). And if you want nginx to [provide TLS
+encryption for Fossil](#tls), proxying HTTP instead of SCGI provides no
+benefit.
+
+However, it is still worth showing the proper method of proxying
+Fossil’s HTTP server through nginx if only to make reading nginx
+documentation on other sites easier:
+
+ location /code {
+ rewrite ^/code(/.*) $1 break;
+ proxy_pass http://127.0.0.1:12345;
+ }
+
+The most common thing people get wrong when hand-rolling a configuration
+like this is to get the slashes wrong. Fossil is sensitive to this. For
+instance, Fossil will not collapse double slashes down to a single
+slash, as some other HTTP servers will.
+
+
+## <a id="large-uv"></a> Allowing Large Unversioned Files
+
+By default, nginx only accepts HTTP messages [up to a
+meg](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size)
+in size. Fossil chunks its sync protocol such that this is not normally
+a problem, but when sending [unversioned content][uv], it uses a single
+message for the entire file. Therefore, if you will be storing files
+larger than this limit as unversioned content, you need to raise the
+limit. Within the `location` block:
+
+ # Allow large unversioned file uploads, such as PDFs
+ client_max_body_size 20M;
+
+[uv]: ../../unvers.wiki
+
+
+## <a id="fail2ban"></a> Integrating `fail2ban`
+
+One of the nice things that falls out of proxying Fossil behind nginx is
+that it makes it easier to configure `fail2ban` to recognize attacks on
+Fossil and automatically block them. Fossil logs the sorts of errors we
+want to detect, but it does so in places like the repository’s admin
+log, a SQL table, which `fail2ban` doesn’t know how to query. By putting
+Fossil behind an nginx proxy, we convert these failures to log file
+form, which `fail2ban` is designed to handle.
+
+First, install `fail2ban`, if you haven’t already:
+
+ sudo apt install fail2ban
+
+We’d like `fail2ban` to react to Fossil `/login` failures. The stock
+configuration of `fail2ban` only detects a few common sorts of SSH
+attacks by default, and its included (but disabled) nginx attack
+detectors don’t include one that knows how to detect an attack on
+Fossil. We have to teach it by putting the following into
+`/etc/fail2ban/filter.d/nginx-fossil-login.conf`:
+
+ [Definition]
+ failregex = ^<HOST> - .*POST .*/login HTTP/..." 401
+
+That teaches `fail2ban` how to recognize the errors logged by Fossil.
+
+Then in `/etc/fail2ban/jail.local`, add this section:
+
+ [nginx-fossil-login]
+ enabled = true
+ logpath = /var/log/nginx/*-https-access.log
+
+The last line is the key: it tells `fail2ban` where we’ve put all of our
+per-repo access logs in the nginx config above.
+
+There’s a [lot more you can do][dof2b], but that gets us out of scope of
+this guide.
+
+
+[dof2b]: https://www.digitalocean.com/community/tutorials/how-to-protect-an-nginx-server-with-fail2ban-on-ubuntu-14-04
+
+
+## <a id="tls"></a> Adding TLS (HTTPS) Support
+
+One of the [many ways](../../ssl.wiki) to provide TLS-encrypted HTTP
+access (a.k.a. HTTPS) to Fossil is to run it behind a web proxy that
+supports TLS. Because one such option is nginx, it’s best to delegate
+TLS to it if you were already using nginx for some other reason, such as
+static content serving, with only part of the site being served by
+Fossil.
+
+The simplest way by far to do this is to use [Let’s Encrypt][LE]’s
+[Certbot][CB], which can configure nginx for you and keep its
+certificates up to date. You need but follow their [nginx on Ubuntu 20
+guide][CBU]. We had trouble with this in the past, but either Certbot
+has gotten smarter or our nginx configurations have gotten simpler, so
+we have removed the manual instructions we used to have here.
+
+You may wish to include something like this from each `server { }`
+block in your configuration to enable TLS in a common, secure way:
+
+```
+# Tell nginx to accept TLS-encrypted HTTPS on the standard TCP port.
+listen 443 ssl;
+listen [::]:443 ssl;
+
+# Reference the TLS cert files produced by Certbot.
+ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
+ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
+
+# Load the Let's Encrypt Diffie-Hellman parameters generated for
+# this server. Without this, the server is vulnerable to Logjam.
+ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
+
+# Tighten things down further, per Qualys’ and Certbot’s advice.
+ssl_session_cache shared:le_nginx_SSL:1m;
+ssl_protocols TLSv1.2 TLSv1.3;
+ssl_prefer_server_ciphers on;
+ssl_session_timeout 1440m;
+
+# Offer OCSP certificate stapling.
+ssl_stapling on;
+ssl_stapling_verify on;
+
+# Enable HSTS.
+include local/enable-hsts;
+```
+
+The [HSTS] step is optional and should be applied only after due
+consideration, since it has the potential to lock users out of your
+site if you later change your mind on the TLS configuration.
+The `local/enable-hsts` file it references is simply:
+
+ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
+
+It’s a separate file because nginx requires that headers like this be
+applied separately for each `location { }` block. We’ve therefore
+factored this out so you can `include` it everywhere you need it.
+
+The [OCSP] step is optional, but recommended.
+
+You may find [Qualys’ SSL Server Test][QSLT] helpful in verifying that
+you have set all this up correctly, and that the configuration is
+strong. We’ve found their [best practices doc][QSLC] to be helpful. As
+of this writing, the above configuration yields an A+ rating when run on
+Ubuntu 22.04.01 LTS.
+
+[CB]: https://certbot.eff.org/
+[CBU]: https://certbot.eff.org/instructions?ws=nginx&os=ubuntufocal
+[LE]: https://letsencrypt.org/
+[HSTS]: https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/
+[OCSP]: https://en.wikipedia.org/wiki/OCSP_stapling
+[QSLC]: https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices
+[QSLT]: https://www.ssllabs.com/ssltest/
+
+<div style="height:50em" id="this-space-intentionally-left-blank"></div>
+
+*[Return to the top-level Fossil server article.](../)*
diff --git a/www/server/debian/service.md b/www/server/debian/service.md
new file mode 100644
index 0000000..f056ff1
--- /dev/null
+++ b/www/server/debian/service.md
@@ -0,0 +1,259 @@
+# Serving via systemd on Debian and Ubuntu
+
+[`systemd`][sdhome] is the service management framework in all major
+in-support versions of Linux. There are multiple ways to run Fossil
+under `systemd`.
+
+[sdhome]: https://www.freedesktop.org/wiki/Software/systemd/
+[wpa]: https://en.wikipedia.org/wiki/Systemd#Adoption
+
+
+## Containerized Service
+
+Two of the methods for running [containerized Fossil][cntdoc] integrate
+with `systemd`, potentially obviating the more direct methods below:
+
+* If you take [the Podman method][podman] of running containerized
+ Fossil, it opens the `podman generate systemd` option for you, as
+ exemplified in [the `fslsrv` script][fslsrv] used on this author’s
+ public Fossil-based web site. That script pulls its container images
+ from [my Docker Hub repo][dhrepo] to avoid the need for my public
+ Fossil server to have build tools and a copy of the Fossil source
+ tree. You’re welcome to use my images as-is, or you may use these
+ tools to bounce custom builds up through a separate container image
+ repo you manage.
+
+* If you’re willing to give up [a lot of features][nsweak] relative to
+ Podman, and you’re willing to tolerate a lot more manual
+ administrivia, [the nspawn method][nspawn] has a lot less overhead,
+ being a direct feature of `systemd` itself.
+
+Both of these options provide [better security][cntsec] than running
+Fossil directly under `systemd`, among [other benefits][cntdoc].
+
+[cntdoc]: ../../containers.md
+[cntsec]: ../../containers.md#security
+[dhrepo]: https://hub.docker.com/r/tangentsoft/fossil
+[fslsrv]: https://tangentsoft.com/fossil/dir?name=bin
+[nspawn]: ../../containers.md#nspawn
+[nsweak]: ../../containers.md#nspawn-weaknesses
+[podman]: ../../containers.md#podman
+
+
+## User Service
+
+A fun thing you can easily do with `systemd` that you can’t directly do
+with older technologies like `inetd` and `xinetd` is to set a server up
+as a “user” service.
+
+You can’t listen on TCP port 80 with this method due to security
+restrictions on TCP ports in every OS where `systemd` runs, but you can
+create a listener socket on a high-numbered (&ge; 1024) TCP port,
+suitable for sharing a Fossil repo to a workgroup on a private LAN.
+
+To do this, write the following in
+`~/.local/share/systemd/user/fossil.service`:
+
+> ```dosini
+[Unit]
+Description=Fossil user server
+After=network-online.target
+
+[Service]
+WorkingDirectory=/home/fossil/museum
+ExecStart=/home/fossil/bin/fossil server --port 9000 repo.fossil
+Restart=always
+RestartSec=3
+
+[Install]
+WantedBy=multi-user.target
+```
+
+Unlike with `inetd` and `xinetd`, we don’t need to tell `systemd` which
+user and group to run this service as, because we’ve installed it
+under the account we’re logged into, which `systemd` will use as the
+service’s owner.
+
+The result is essentially [the standalone server method](../any/none.md)
+coupled with an intelligent service manager that will start it
+automatically in the background on system boot, perform automatic
+service restarts with back-off logic, and more, making this much more
+robust than the by-hand launches of `fossil` in the platform-independent
+Fossil server instructions. The service will stay up until we
+explicitly tell it to shut down.
+
+This scheme couples well with [the generic SCGI instructions][scgi] as
+it requires a way to run the underlying repository server in the
+background. Given that its service port is then proxied by SCGI, it
+follows that it doesn’t need to run as a system service. A user service
+works perfectly well for this.
+
+Because we’ve set this up as a user service, the commands you give to
+manipulate the service vary somewhat from the sort you’re more likely to
+find online:
+
+ $ systemctl --user daemon-reload
+ $ systemctl --user enable fossil
+ $ systemctl --user start fossil
+ $ systemctl --user status fossil -l
+ $ systemctl --user stop fossil
+
+That is, we don’t need to talk to `systemd` with `sudo` privileges, but
+we do need to tell it to look at the user configuration rather than the
+system-level configuration.
+
+This scheme isolates the permissions needed by the Fossil server, which
+reduces the amount of damage it can do if there is ever a
+remotely-triggerable security flaw found in Fossil.
+
+On some `systemd` based OSes, user services only run while that user is
+logged in interactively. This is common on systems aiming to provide
+desktop environments, where this is the behavior you often want. To
+allow background services to continue to run after logout, say:
+
+ $ sudo loginctl enable-linger $USER
+
+You can paste the command just like that into your terminal, since
+`$USER` will expand to your login name.
+
+[scgi]: ../any/scgi.md
+
+
+
+### System Service Alternative
+
+There are some common reasons that you’d have good cause to install
+Fossil as a system-level service rather than the prior user-level one:
+
+* You’re using [the new `fossil server --cert` feature][sslsrv] to get
+ TLS service and want it to listen directly on port 443, rather than be
+ proxied, as one had to do before Fossil got the ability to act as a
+ TLS server itself. That requires root privileges, so you can’t run
+ it as a user-level service.
+
+* You’re proxying Fossil with [nginx](./nginx.md) or similar, allowing
+ it to bind to high-numbered ports, but because it starts as a system
+ service, you can’t get Fossil into the same dependency chain to
+ ensure things start up and shut down in the proper order unless it
+ *also* runs as a system service.
+
+* You want to make use of Fossil’s [chroot jail feature][cjail], which
+ requires the server to start as root.
+
+There are just a small set of changes required:
+
+1. Install the unit file to one of the persistent system-level unit
+ file directories. Typically, these are:
+
+ /etc/systemd/system
+ /lib/systemd/system
+
+2. Add `User` and `Group` directives to the `[Service]` section so
+ Fossil runs as a normal user, preferably one with access only to
+ the Fossil repo files, rather than running as `root`.
+
+[sslsrv]: ../../ssl-server.md
+[cjail]: ../../chroot.md
+
+
+## Socket Activation
+
+Another useful method to serve a Fossil repo via `systemd` is via a
+socket listener, which `systemd` calls “[socket activation][sa],”
+roughly equivalent to [the ancient `inetd` method](../any/inetd.md).
+It’s more complicated, but it has some nice properties.
+
+We first need to define the privileged socket listener by writing
+`/etc/systemd/system/fossil.socket`:
+
+> ```dosini
+[Unit]
+Description=Fossil socket
+
+[Socket]
+Accept=yes
+ListenStream=80
+NoDelay=true
+
+[Install]
+WantedBy=sockets.target
+```
+
+Note the change of configuration directory from the `~/.local` directory
+to the system level. We need to start this socket listener at the root
+level because of the low-numbered TCP port restriction we brought up
+above.
+
+This configuration says more or less the same thing as the socket part
+of an `inetd` entry [exemplified elsewhere in this
+documentation](../any/inetd.md).
+
+Next, create the service definition file in that same directory as
+`fossil@.service`:
+
+> ```dosini
+[Unit]
+Description=Fossil socket server
+After=network-online.target
+
+[Service]
+WorkingDirectory=/home/fossil/museum
+ExecStart=/home/fossil/bin/fossil http repo.fossil
+StandardInput=socket
+
+[Install]
+WantedBy=multi-user.target
+```
+
+Notice that we haven’t told `systemd` which user and group to run Fossil
+under. Since this is a system-level service definition, that means it
+will run as root, which then causes Fossil to [automatically drop into a
+`chroot(2)` jail](../../chroot.md) rooted at the `WorkingDirectory`
+we’ve configured above, shortly after each `fossil http` call starts.
+
+The `Restart*` directives we had in the user service configuration above
+are unnecessary for this method, since Fossil isn’t supposed to remain
+running under it. Each HTTP hit starts one Fossil instance, which
+handles that single client’s request and then immediately shuts down.
+
+Next, you need to tell `systemd` to reload its system-level
+configuration files and enable the listening socket:
+
+ $ sudo systemctl daemon-reload
+ $ sudo systemctl enable fossil.socket
+
+And now you can manipulate the socket listener:
+
+ $ sudo systemctl start fossil.socket
+ $ sudo systemctl status -l fossil.socket
+ $ sudo systemctl stop fossil.socket
+
+Notice that we’re working with the *socket*, not the *service*. The fact
+that we’ve given them the same base name and marked the service as an
+instantiated service with the “`@`” notation allows `systemd` to
+automatically start an instance of the service each time a hit comes in
+on the socket that `systemd` is monitoring on Fossil’s behalf. To see
+this service instantiation at work, visit a long-running Fossil page
+(e.g. `/tarball`) and then give a command like this:
+
+ $ sudo systemctl --full | grep fossil
+
+This will show information about the `fossil` socket and service
+instances, which should show your `/tarball` hit handler, if it’s still
+running:
+
+ fossil@20-127.0.0.1:80-127.0.0.1:38304.service
+
+You can feed that service instance description to a `systemctl kill`
+command to stop that single instance without restarting the whole
+`fossil` service, for example.
+
+In all of this, realize that we’re able to manipulate a single socket
+listener or single service instance at a time, rather than reload the
+whole externally-facing network configuration as with the far more
+primitive `inetd` service.
+
+[sa]: http://0pointer.de/blog/projects/socket-activation.html
+
+
+*[Return to the top-level Fossil server article.](../)*
diff --git a/www/server/index.html b/www/server/index.html
new file mode 100644
index 0000000..c15d42f
--- /dev/null
+++ b/www/server/index.html
@@ -0,0 +1,316 @@
+<div class='fossil-doc' data-title="How To Configure A Fossil Server">
+
+<style type="text/css">
+ .doc > .content th.fep {
+ font-family: "Helvetica Neue", "Arial Narrow", "Myriad Pro", "Avenir Next Condensed";
+ font-stretch: condensed;
+ min-width: 3em;
+ padding: 0.4em;
+ white-space: nowrap;
+ }
+
+ .doc > .content th.host {
+ font-family: "Helvetica Neue", "Arial Narrow", "Myriad Pro", "Avenir Next Condensed";
+ font-stretch: condensed;
+ padding: 0.4em;
+ text-align: right;
+ }
+
+ .doc > .content td.doc {
+ text-align: center;
+ }
+</style>
+
+
+<h2>No Server Required</h2>
+
+<p>Fossil does not require a central server, but <a
+href="whyuseaserver.wiki">a server can be useful</a>.</p>
+
+<p>A Fossil server does not require much memory, CPU, or disk space
+and can run comfortably on a generic $5/month virtual host
+or on a small device like a Raspberry Pi, or it can co-exist
+on a host running other services without getting in the way.
+
+<p>This article is a quick-reference guide for setting up your own
+Fossil server, with links to more detailed instructions specific to
+particular systems, should you want extra help.</p>
+
+
+<h2 id="prep">Repository Prep</h2>
+
+<p>Prior to serving a Fossil repository to others, consider running <a
+href="$ROOT/help/ui"><tt>fossil ui</tt></a> locally and taking these
+minimum recommended preparation steps:</p>
+
+<ol>
+ <li><p>Fossil creates only one user in a <a
+ href="$ROOT/help/new">new repository</a> and gives it the <a
+ href="../caps/admin-v-setup.md#apsu">all-powerful Setup capability</a>.
+ The 10-digit random password generated for that user is fairly strong
+ against remote attack, even without explicit password guess rate
+ limiting, but because that user has so much power, you may want to
+ give it a much stronger password under Admin → Users.</a></li>
+
+ <li><p>Run the Admin → Security-Audit tool to verify that other
+ security-related permissions and settings are as you want them.
+ Consider clicking the “Take it private” link on that page to lock down
+ the security on that site to a level appropriate to a private
+ repository, even if you will eventually want some public service. It's
+ better to start from a secure position and open up service
+ feature-by-feature as necessary than it is to start from a fully open
+ position and lock down features one by one to achieve a secure
+ stance.</p></li>
+</ol>
+
+<p>With the repository secured, it is safe to upload a copy of the
+repository file to your server and proceed with server setup, below.
+Further configuration steps can wait until <a href="#postsetup">after
+the server is running</a>.</p>
+
+
+<h2 id="methods">Activation Methods</h2>
+
+<p>There are basically five ways to run a Fossil server:</p>
+
+<ol>
+ <li><a href="any/cgi.md">CGI</a>
+ <li><a href="#slist">Socket listener</a>
+ <li><a href="any/none.md">Stand-alone HTTP server</a>
+ <li><a href="any/scgi.md">SCGI</a>
+ <li><a href="#ssh">SSH</a>
+</ol>
+
+<p>All of these methods can serve either a single repository or a
+directory hierarchy containing multiple repositories.</p>
+
+<p>You are not restricted to a single server setup. The same Fossil
+repository can be served using two or more of the above techniques at
+the same time. These methods use clean, well-defined, standard
+interfaces (CGI, SCGI, and HTTP) which allow you to easily migrate from
+one method to another in response to changes in hosting providers or
+administrator preferences.</p>
+
+<h3 id="cgi">CGI</h3>
+
+<p>Most ordinary web servers can <a href="any/cgi.md">run Fossil as a
+CGI script</a>. This method is known to work with Apache,
+<tt>lighttpd</tt>, and <a
+href="any/althttpd.md"><tt>althttpd</tt></a>. The Fossil server
+administrator places a <a href="$ROOT/help/cgi">short CGI script</a> in
+the web server's document hierarchy and when a client requests the URL
+that corresponds to that script, Fossil runs and generates the
+response.</p>
+
+<p>CGI is a good choice for merging Fossil into an existing web site,
+particularly on hosts that have CGI set up and working.
+The Fossil <a href="../selfhost.wiki">self-hosting repositories</a> are
+implemented with CGI underneath <tt>althttpd</tt>.</p>
+
+<h3 id="slist">Socket Listener</h3>
+
+<p>Socket listener daemons such as
+<a id="inetd" href="any/inetd.md"><tt>inetd</tt></a>, <a id="xinetd"
+href="any/xinetd.md"><tt>xinetd</tt></a>, <a id="stunnel"
+href="any/stunnel.md"><tt>stunnel</tt></a>, <a
+href="macos/service.md"><tt>launchd</tt></a>, and <a
+href="debian/service.md"><tt>systemd</tt></a>
+can be configured to invoke the the
+<a href="$ROOT/help/http"><tt>fossil http</tt></a> command to handle
+each incoming HTTP request. The "<tt>fossil http</tt>" command reads
+the HTTP request off of standard input, computes an appropriate
+reply, and writes the reply on standard output. There is a separate
+invocation of the "<tt>fossil http</tt>" command for each HTTP request.
+The socket listener daemon takes care of relaying content to and from
+the client, and (in the case of <a href="any/stunnel.md">stunnel</a>)
+handling TLS decryption and encryption.
+
+<h3 id="standalone">Stand-alone HTTP Server</h3>
+
+<p>This is the <a href="any/none.md">easiest method</a>.
+A stand-alone server uses the
+<a href="$ROOT/help/server"><tt>fossil server</tt></a> command to run a
+process that listens for incoming HTTP requests on a socket and then
+dispatches a copy of itself to deal with each incoming request. You can
+expose Fossil directly to the clients in this way or you can interpose a
+<a href="https://en.wikipedia.org/wiki/Reverse_proxy">reverse proxy</a>
+layer between the clients and Fossil.</p>
+
+<h3 id="scgi">SCGI</h3>
+
+<p>The Fossil standalone server can also handle <a href="any/scgi.md">SCGI</a>.
+When the <a href="$ROOT/help/server"><tt>fossil server</tt></a> command is
+run with the extra <tt>--scgi</tt> option, it listens for incoming
+SCGI requests rather than HTTP requests. This allows Fossil to
+respond to requests from web servers <a href="debian/nginx.md">such as
+nginx</a> that don't support CGI. SCGI is a simpler protocol to proxy
+than HTTP, since the HTTP doesn't have to be re-interpreted in terms of
+the proxy's existing HTTP implementation, but it's more complex to set
+up because you also have to set up an SCGI-to-HTTP proxy for it. It is
+worth taking on this difficulty only when you need to integrate Fossil
+into an existing web site already being served by an SCGI-capable web
+server.</p>
+
+<h3 id="ssh">SSH</h3>
+
+<p>Fossil supports <tt>ssh://</tt> URLs, but there are <a
+href="../caps/#webonly">practical limitations</a> with the default
+behavior. You can get the full power of <a href="../caps/">Fossil's RBAC
+system</a> over SSH <a href="any/http-over-ssh.md">with a bit of clever
+configuration</a>.</p>
+
+<h2 id="matrix">Activation Tutorials</h2>
+
+<p>We've broken the configuration for each method out into a series of
+sub-articles. Some of these are generic, while others depend on
+particular operating systems or front-end software:</p>
+
+<div class="indent"><table>
+ <tr>
+ <th class="host">⇩ OS / Method ⇨</th>
+ <th class="fep">direct</th>
+ <th class="fep">inetd</th>
+ <th class="fep">stunnel</th>
+ <th class="fep">CGI</th>
+ <th class="fep">SCGI</th>
+ <th class="fep">FastCGI</th>
+ <th class="fep">althttpd</th>
+ <th class="fep">proxy</th>
+ <th class="fep">service</th>
+ </tr>
+
+ <tr>
+ <th class="host"><a href="any/">Any</a></th>
+ <td class="doc"><a href="any/none.md">✅</a></td>
+ <td class="doc"><a href="any/inetd.md">✅</a></td>
+ <td class="doc"><a href="any/stunnel.md">✅</a></td>
+ <td class="doc"><a href="any/cgi.md">✅</a></td>
+ <td class="doc"><a href="any/scgi.md">✅</a></td>
+ <td class="doc">❌</td>
+ <td class="doc"><a href="any/althttpd.md">✅</a></td>
+ <td class="doc">❌</td>
+ <td class="doc">❌</td>
+ </tr>
+
+ <tr>
+ <th class="host"><a href="debian/">Debian/Ubuntu</a></th>
+ <td class="doc"><a href="any/none.md">✅</a></td>
+ <td class="doc"><a href="any/inetd.md">✅</a></td>
+ <td class="doc"><a href="any/stunnel.md">✅</a></td>
+ <td class="doc"><a href="any/cgi.md">✅</a></td>
+ <td class="doc"><a href="any/scgi.md">✅</a></td>
+ <td class="doc">❌</td>
+ <td class="doc"><a href="any/althttpd.md">✅</a></td>
+ <td class="doc"><a href="debian/nginx.md">✅</a></td>
+ <td class="doc"><a href="debian/service.md">✅</a></td>
+ </tr>
+
+ <tr>
+ <th class="host"><a href="macos/">macOS</a></th>
+ <td class="doc"><a href="any/none.md">✅</a></td>
+ <td class="doc">❌</td>
+ <td class="doc"><a href="any/stunnel.md">✅</a></td>
+ <td class="doc"><a href="any/cgi.md">✅</a></td>
+ <td class="doc"><a href="any/scgi.md">✅</a></td>
+ <td class="doc">❌</td>
+ <td class="doc"><a href="any/althttpd.md">✅</a></td>
+ <td class="doc">❌</td>
+ <td class="doc"><a href="macos/service.md">✅</a></td>
+ </tr>
+
+ <tr>
+ <th class="host"><a href="openbsd/">OpenBSD</a></th>
+ <td class="doc"><a href="any/none.md">✅</a></td>
+ <td class="doc">❌</td>
+ <td class="doc"><a href="any/stunnel.md">✅</a></td>
+ <td class="doc"><a href="any/cgi.md">✅</a></td>
+ <td class="doc"><a href="any/scgi.md">✅</a></td>
+ <td class="doc"><a href="openbsd/fastcgi.md">✅</a></td>
+ <td class="doc"><a href="any/althttpd.md">✅</a></td>
+ <td class="doc">❌</td>
+ <td class="doc"><a href="openbsd/service.wiki">✅</a></td>
+ </tr>
+
+ <tr>
+ <th class="host"><a href="windows/">Windows</a></th>
+ <td class="doc"><a href="windows/none.md">✅</a></td>
+ <td class="doc">❌</td>
+ <td class="doc"><a href="windows/stunnel.md">✅</a></td>
+ <td class="doc"><a href="windows/cgi.md">✅</a></td>
+ <td class="doc">❌</td>
+ <td class="doc">❌</td>
+ <td class="doc">❌</td>
+ <td class="doc"><a href="windows/iis.md">✅</a></td>
+ <td class="doc"><a href="windows/service.md">✅</a></td>
+ </tr>
+</table></div>
+
+<p>Where there is a check mark in the "<b>Any</b>" row, the method for that is
+generic enough that it works across OSes that Fossil is known to work
+on. The check marks below that usually just link to this generic
+documentation.</p>
+
+<p>The method in the "<b>proxy</b>" column is for the platform's default
+web server configured as a <a
+href="https://en.wikipedia.org/wiki/Reverse_proxy">reverse proxy</a> for
+Fossil's built-in HTTP server: <a href="debian/nginx.md">nginx</a>, <a
+href="windows/iis.md">IIS</a>, Apache, etc.</p>
+
+<p>We welcome <a href="../contribute.wiki">contributions</a> to fill gaps
+(<font size="-2">❌</font>) in the table above.</p>
+</noscript>
+
+
+<h2 id="postsetup">Post-Activation Configuration</h2>
+
+<p>After the server is up and running, log into it as the Setup user and
+visit the Admin menu to finish configuring that repository for
+service:</p>
+
+<ol>
+ <li><p>Add user accounts for your other team members. Use <a
+ href="../caps/index.md#ucat">categories</a> to define access policies
+ rather than redundantly give each new user the same <a
+ href="../caps/index.md#ucap">individual capabilities</a>.</p></li>
+
+ <li><p>Test access to the repository from each category of non-Setup
+ user that you created. You may have to give your user categories some
+ overlooked capabilities, particularly if you followed <a
+ href="#prep">our earlier advice</a> to take the repository private
+ prior to setting up the server.</p></li>
+
+ <li><p>Modify the repository's look and feel by <a
+ href="../customskin.md">customizing the skin</a>.</p></li>
+
+ <li><p>If the repository includes <a
+ href="../embeddeddoc.wiki">embedded documentation</a>, consider
+ activating the search feature (Admin → Search) so that visitors can do
+ full-text search on your documentation.</p></li>
+
+ <li><p>Now that others can be making changes to the repository,
+ consider monitoring them via <a href="../alerts.md">email alerts</a>
+ or the <a href="$ROOT/help/www/timeline.rss">timeline RSS
+ feed</a>.</p></li>
+
+ <li><p>Turn on the various logging features.</p></li>
+</ol>
+
+<p>Reload the Admin → Security-Audit page occasionally during this
+process to double check that you have not mistakenly configured the
+server in a way that might expose information that you want to keep
+private.</p>
+
+
+<h2 id="more">Further Details</h2>
+
+<ul>
+ <li><a id="chroot" href="../chroot.md" >The Server Chroot Jail</a>
+ <li><a id="loadmgmt" href="../loadmgmt.md" >Managing Server Load</a>
+ <li><a id="bkofc" href="../backoffice.md" >The Backoffice</a>
+ <li><a id="tls" href="../ssl.wiki" >Securing a Repository with TLS</a>
+ <li><a id="ext" href="../serverext.wiki">CGI Server Extensions</a>
+ <li><a id="about" href="../aboutcgi.wiki" >How CGI Works In Fossil</a>
+ <li><a id="sync" href="../sync.wiki" >The Fossil Sync Protocol</a>
+</ul>
+
+</div>
diff --git a/www/server/macos/index.md b/www/server/macos/index.md
new file mode 100644
index 0000000..52c9ef9
--- /dev/null
+++ b/www/server/macos/index.md
@@ -0,0 +1,5 @@
+# macOS Specific Fossil Service Options
+
+- [Running Fossil under `launchd`](./service.md)
+
+*[Return to the top-level Fossil server article.](../)*
diff --git a/www/server/macos/service.md b/www/server/macos/service.md
new file mode 100644
index 0000000..b1358fa
--- /dev/null
+++ b/www/server/macos/service.md
@@ -0,0 +1,181 @@
+# Serving via launchd on macOS
+
+[`launchd`][ldhome] is the default service management framework on macOS
+[since the release of Tiger in 2005][wpa]. If you want a Fossil server
+to launch in the background on a Mac, it’s the way Apple wants you to do
+it. `launchd` is to macOS as `systemd` is to most modern Linux desktop
+systems. (Indeed, `systemd` arguably reinvented the perfectly good,
+pre-existing `launchd` wheel.)
+
+Unlike in [our `systemd` article](../debian/service.md), we’re not going
+to show the per-user method here, because those so-called
+[LaunchAgents][la] only start when a user is logged into the GUI, and
+they stop when that user logs out. This does not strike us as proper
+“server” behavior, so we’ll stick to system-level LaunchDaemons instead.
+
+However, we will still give two different configurations, just as in the
+`systemd` article: one for a standalone HTTP server, and one using
+socket activation.
+
+For more information on `launchd`, the single best resource we’ve found
+is [launchd.info](https://launchd.info). The next best is:
+
+ $ man launchd.plist
+
+[la]: http://www.grivet-tools.com/blog/2014/launchdaemons-vs-launchagents/
+[ldhome]: https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html
+[wpa]: https://en.wikipedia.org/wiki/Launchd
+
+
+
+## Standalone HTTP Server
+
+To configure `launchd` to start Fossil as a standalone HTTP server,
+write the following as `com.example.dev.FossilHTTP.plist`:
+
+```xml
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
+ "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.example.dev.FossilHTTP</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/usr/local/bin/fossil</string>
+ <string>server</string>
+ <string>--port</string>
+ <string>9000</string>
+ <string>repo.fossil</string>
+ </array>
+ <key>WorkingDirectory</key>
+ <string>/Users/you/museum</string>
+ <key>KeepAlive</key>
+ <true/>
+ <key>RunAtLoad</key>
+ <true/>
+ <key>StandardErrorPath</key>
+ <string>/tmp/fossil-error.log</string>
+ <key>StandardOutPath</key>
+ <string>/tmp/fossil-info.log</string>
+ <key>UserName</key>
+ <string>you</string>
+ <key>GroupName</key>
+ <string>staff</string>
+ <key>InitGroups</key>
+ <true/>
+</dict>
+</plist>
+```
+
+In this example, we’re assuming your development organization uses the
+domain name “`dev.example.org`”, that your short macOS login name is
+“`you`”, and that you store your Fossils in “`~/museum`”. Adjust these
+elements of the plist file to suit your local situation.
+
+You might be wondering about the use of `UserName`: isn’t Fossil
+supposed to drop privileges and enter [a `chroot(2)`
+jail](../../chroot.md) when it’s started as root like this? Why do we
+need to give it a user name? Won’t Fossil use the owner of the
+repository file to set that? All I can tell you is that in testing here,
+if you leave the user and group configuration at the tail end of that
+plist file out, Fossil will remain running as root!
+
+Install that file and set it to start with:
+
+ $ sudo install -o root -g wheel -m 644 com.example.dev.FossilHTTP.plist \
+ /Library/LaunchDaemons/
+ $ sudo launchctl load -w /Library/LaunchDaemons/com.example.dev.FossilHTTP.plist
+
+Because we set the `RunAtLoad` key, this will also launch the daemon.
+
+Stop the daemon with:
+
+ $ sudo launchctl unload -w /Library/LaunchDaemons/com.example.dev.FossilHTTP.plist
+
+
+## Socket Listener
+
+Another useful method to serve a Fossil repo via `launchd` is by setting
+up a socket listener:
+
+```xml
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
+ "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.example.dev.FossilSocket</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/usr/local/bin/fossil</string>
+ <string>http</string>
+ <string>repo.fossil</string>
+ </array>
+ <key>Sockets</key>
+ <dict>
+ <key>Listeners</key>
+ <dict>
+ <key>SockServiceName</key>
+ <string>9001</string>
+ <key>SockType</key>
+ <string>stream</string>
+ <key>SockProtocol</key>
+ <string>TCP</string>
+ <key>SockFamily</key>
+ <string>IPv4</string>
+ </dict>
+ </dict>
+ <key>inetdCompatibility</key>
+ <dict>
+ <key>Wait</key>
+ <false/>
+ </dict>
+ <key>WorkingDirectory</key>
+ <string>/Users/you/museum</string>
+ <key>UserName</key>
+ <string>you</string>
+ <key>GroupName</key>
+ <string>staff</string>
+ <key>InitGroups</key>
+ <true/>
+</dict>
+</plist>
+```
+
+Save it as “`com.example.dev.FossilSocket.plist`” and install and load
+it into `launchd` as above.
+
+This version differs in several key ways:
+
+1. We’re calling Fossil as `fossil http` rather than `fossil server` to
+ make it serve a single request and then shut down immediately.
+
+2. We’ve told `launchd` to listen on our TCP port number instead of
+ passing it to `fossil`.
+
+3. We’re running the daemon in `inetd` compatibility mode of `launchd`
+ with “wait” mode off, which tells it to attach the connected socket
+ to the `fossil` process’s stdio handles.
+
+4. We’ve removed the `Standard*Path` keys because they interfere with
+ our use of stdio handles for HTTP I/O. You might therefore want to
+ start with the first method and then switch over to this one only
+ once you’ve got the daemon launching debugged, since once you tie up
+ stdio this way, you won’t be able to get logging information from
+ Fossil via that path. (Fossil does have some internal logging
+ mechanisms, but you can’t get at them until Fossil is launching!)
+
+5. We’ve removed the `KeepAlive` and `RunAtLoad` keys because those
+ options aren’t appropriate to this type of service.
+
+6. Because we’re running it via a socket listener instead of as a
+ standalone HTTP server, the Fossil service only takes system
+ resources when it’s actually handling an HTTP hit. If your Fossil
+ server is mostly idle, this method will be a bit more efficient than
+ the first option.
+
+
+*[Return to the top-level Fossil server article.](../)*
diff --git a/www/server/openbsd/fastcgi.md b/www/server/openbsd/fastcgi.md
new file mode 100644
index 0000000..70db94b
--- /dev/null
+++ b/www/server/openbsd/fastcgi.md
@@ -0,0 +1,302 @@
+# Serving via httpd on OpenBSD
+
+[`httpd`][httpd] is the default web server that is included in the base
+install on OpenBSD. It's minimal and lightweight but secure and capable,
+and provides a clean interface for setting up a Fossil server using
+FastCGI.
+
+This article will detail the steps required to setup a TLS-enabled
+`httpd` configuration that serves multiple Fossil repositories out of
+a single directory within a chroot, and allow `ssh` access to create
+new repositories remotely.
+
+**NOTE:** The following instructions assume an OpenBSD 6.7 installation.
+
+[httpd]: https://www.openbsd.org/papers/httpd-asiabsdcon2015.pdf
+
+## <a id="fslinstall"></a>Install Fossil
+
+Use the OpenBSD package manager `pkg_add` to install Fossil, making sure
+to select the statically linked binary.
+
+```console
+$ doas pkg_add fossil
+quirks-3.325 signed on 2020-06-12T06:24:53Z
+Ambiguous: choose package for fossil
+ 0: <None>
+ 1: fossil-2.10v0
+ 2: fossil-2.10v0-static
+Your choice: 2
+fossil-2.10v0-static: ok
+```
+
+This installs Fossil into the chroot. To facilitate local use, create a
+symbolic link of the fossil executable into `/usr/local/bin`.
+
+```console
+$ doas ln -s /var/www/bin/fossil /usr/local/bin/fossil
+```
+
+As a privileged user, create the file `/var/www/cgi-bin/scm` with the
+following contents to make the CGI script that `httpd` will execute in
+response to `fsl.domain.tld` requests; all paths are relative to the
+`/var/www` chroot.
+
+```sh
+#!/bin/fossil
+directory: /htdocs/fsl.domain.tld
+notfound: https://domain.tld
+repolist
+errorlog: /logs/fossil.log
+```
+
+The `directory` directive instructs Fossil to serve all repositories
+found in `/var/www/htdocs/fsl.domain.tld`, while `errorlog` sets logging
+to be saved to `/var/www/logs/fossil.log`; create the repository
+directory and log file—making the latter owned by the `www` user, and
+the script executable.
+
+```console
+$ doas mkdir /var/www/htdocs/fsl.domain.tld
+$ doas touch /var/www/logs/fossil.log
+$ doas chown www /var/www/logs/fossil.log
+$ doas chmod 660 /var/www/logs/fossil.log
+$ doas chmod 755 /var/www/cgi-bin/scm
+```
+
+## <a id="chroot"></a>Setup chroot
+
+Fossil needs both `/dev/random` and `/dev/null`, which aren't accessible
+from within the chroot, so need to be constructed; `/var`, however, is
+mounted with the `nodev` option. Rather than removing this default
+setting, create a small memory filesystem and then mount it on to
+`/var/www/dev` with [`mount_mfs(8)`][mfs] so that the `random` and
+`null` device files can be created. In order to avoid necessitating a
+startup script to recreate the device files at boot, create a template
+of the needed ``/dev`` tree to automatically populate the memory
+filesystem.
+
+```console
+$ doas mkdir /var/www/dev
+$ doas install -d -g daemon /template/dev
+$ cd /template/dev
+$ doas /dev/MAKEDEV urandom
+$ doas mknod -m 666 null c 2 2
+$ doas mount_mfs -s 1M -P /template/dev /dev/sd0b /var/www/dev
+$ ls -l
+total 0
+crw-rw-rw- 1 root daemon 2, 2 Jun 20 08:56 null
+lrwxr-xr-x 1 root daemon 7 Jun 18 06:30 random@ -> urandom
+crw-r--r-- 1 root wheel 45, 0 Jun 18 06:30 urandom
+```
+
+[mfs]: https://man.openbsd.org/mount_mfs.8
+
+To make the mountable memory filesystem permanent, open `/etc/fstab` as
+a privileged user and add the following line to automate creation of the
+filesystem at startup:
+
+```console
+swap /var/www/dev mfs rw,-s=1048576,-P=/template/dev 0 0
+```
+
+The same user that executes the fossil binary must have writable access
+to the repository directory that resides within the chroot; on OpenBSD
+this is `www`. In addition, grant repository directory ownership to the
+user who will push to, pull from, and create repositories.
+
+```console
+$ doas chown -R user:www /var/www/htdocs/fsl.domain.tld
+$ doas chmod 770 /var/www/htdocs/fsl.domain.tld
+```
+
+## <a id="httpdconfig"></a>Configure httpd
+
+On OpenBSD, [httpd.conf(5)][httpd] is the configuration file for
+`httpd`. To setup the server to serve all Fossil repositores within the
+directory specified in the CGI script, and automatically redirect
+standard HTTP requests to HTTPS—apart from [Let's Encrypt][LE]
+challenges issued in response to [acme-client(1)][acme] certificate
+requests—create `/etc/httpd.conf` as a privileged user with the
+following contents.
+
+[LE]: https://letsencrypt.org
+[acme]: https://man.openbsd.org/acme-client.1
+[httpd.conf(5)]: https://man.openbsd.org/httpd.conf.5
+
+```apache
+server "fsl.domain.tld" {
+ listen on * port http
+ root "/htdocs/fsl.domain.tld"
+ location "/.well-known/acme-challenge/*" {
+ root "/acme"
+ request strip 2
+ }
+ location * {
+ block return 301 "https://$HTTP_HOST$REQUEST_URI"
+ }
+ location "/*" {
+ fastcgi { param SCRIPT_FILENAME "/cgi-bin/scm" }
+ }
+}
+
+server "fsl.domain.tld" {
+ listen on * tls port https
+ root "/htdocs/fsl.domain.tld"
+ tls {
+ certificate "/etc/ssl/domain.tld.fullchain.pem"
+ key "/etc/ssl/private/domain.tld.key"
+ }
+ hsts {
+ max-age 15768000
+ preload
+ subdomains
+ }
+ connection max request body 104857600
+ location "/*" {
+ fastcgi { param SCRIPT_FILENAME "/cgi-bin/scm" }
+ }
+ location "/.well-known/acme-challenge/*" {
+ root "/acme"
+ request strip 2
+ }
+}
+```
+
+[The default limit][dlim] for HTTP messages in OpenBSD’s `httpd` server
+is 1 MiB. Fossil chunks its sync protocol such that this is not
+normally a problem, but when sending [unversioned content][uv], it uses
+a single message for the entire file. Therefore, if you will be storing
+files larger than this limit as unversioned content, you need to raise
+the limit as we’ve done above with the “`connection max request body`”
+setting, raising the limit to 100 MiB.
+
+[dlim]: https://man.openbsd.org/httpd.conf.5#connection
+[uv]: ../../unvers.wiki
+
+**NOTE:** If not already in possession of an HTTPS certificate, comment
+out the `https` server block and proceed to securing a free
+[Let's Encrypt Certificate](#letsencrypt); otherwise skip to
+[Start `httpd`](#starthttpd).
+
+
+## <a id="letsencrypt"></a>Let's Encrypt Certificate
+
+In order for `httpd` to serve HTTPS, secure a free certificate from
+Let's Encrypt using `acme-client`. Before issuing the request, however,
+ensure you have a zone record for the subdomain with your registrar or
+nameserver. Then open `/etc/acme-client.conf` as a privileged user to
+configure the request.
+
+```dosini
+authority letsencrypt {
+ api url "https://acme-v02.api.letsencrypt.org/directory"
+ account key "/etc/acme/letsencrypt-privkey.pem"
+}
+
+authority letsencrypt-staging {
+ api url "https://acme-staging.api.letsencrypt.org/directory"
+ account key "/etc/acme/letsencrypt-staging-privkey.pem"
+}
+
+domain domain.tld {
+ alternative names { www.domain.tld fsl.domain.tld }
+ domain key "/etc/ssl/private/domain.tld.key"
+ domain certificate "/etc/ssl/domain.tld.crt"
+ domain full chain certificate "/etc/ssl/domain.tld.fullchain.pem"
+ sign with letsencrypt
+}
+```
+
+Start `httpd` with the new configuration file, and issue the certificate
+request.
+
+```console
+$ doas rcctl start httpd
+$ doas acme-client -vv domain.tld
+acme-client: /etc/acme/letsencrypt-privkey.pem: account key exists (not creating)
+acme-client: /etc/acme/letsencrypt-privkey.pem: loaded RSA account key
+acme-client: /etc/ssl/private/domain.tld.key: generated RSA domain key
+acme-client: https://acme-v01.api.letsencrypt.org/directory: directories
+acme-client: acme-v01.api.letsencrypt.org: DNS: 172.65.32.248
+...
+N(Q????Z???j?j?>W#????b???? H????eb??T??*? DNosz(???n{L}???D???4[?B] (1174 bytes)
+acme-client: /etc/ssl/domain.tld.crt: created
+acme-client: /etc/ssl/domain.tld.fullchain.pem: created
+```
+
+A successful result will output the public certificate, full chain of
+trust, and private key into the `/etc/ssl` directory as specified in
+`acme-client.conf`.
+
+```console
+$ doas ls -lR /etc/ssl
+-r--r--r-- 1 root wheel 2.3K Mar 2 01:31:03 2018 domain.tld.crt
+-r--r--r-- 1 root wheel 3.9K Mar 2 01:31:03 2018 domain.tld.fullchain.pem
+
+/etc/ssl/private:
+-r-------- 1 root wheel 3.2K Mar 2 01:31:03 2018 domain.tld.key
+```
+
+Make sure to reopen `/etc/httpd.conf` to uncomment the second server
+block responsible for serving HTTPS requests before proceeding.
+
+## <a id="starthttpd"></a>Start `httpd`
+
+With `httpd` configured to serve Fossil repositories out of
+`/var/www/htdocs/fsl.domain.tld`, and the certificates and key in place,
+enable and start `slowcgi`—OpenBSD's FastCGI wrapper server that will
+execute the above Fossil CGI script—before checking that the syntax of
+the `httpd.conf` configuration file is correct, and (re)starting the
+server (if still running from requesting a Let's Encrypt certificate).
+
+```console
+$ doas rcctl enable slowcgi
+$ doas rcctl start slowcgi
+slowcgi(ok)
+$ doas httpd -vnf /etc/httpd.conf
+configuration OK
+$ doas rcctl start httpd
+httpd(ok)
+```
+
+## <a id="clientconfig"></a>Configure Client
+
+To facilitate creating new repositories and pushing them to the server,
+add the following function to your `~/.cshrc` or `~/.zprofile` or the
+config file for whichever shell you are using on your development box.
+
+```sh
+finit() {
+ fossil init $1.fossil && \
+ chmod 664 $1.fossil && \
+ fossil open $1.fossil && \
+ fossil user password $USER $PASSWD && \
+ fossil remote-url https://$USER:$PASSWD@fsl.domain.tld/$1 && \
+ rsync --perms $1.fossil $USER@fsl.domain.tld:/var/www/htdocs/fsl.domain.tld/ >/dev/null && \
+ chmod 644 $1.fossil && \
+ fossil ui
+}
+```
+
+This enables a new repository to be made with `finit repo`, which will
+create the fossil repository file `repo.fossil` in the current working
+directory; by default, the repository user is set to the environment
+variable `$USER`. It then opens the repository and sets the user
+password to the `$PASSWD` environment variable (which you can either set
+with `export PASSWD 'password'` on the command line or add to a
+*secured* shell environment file), and the `remote-url` to
+`https://fsl.domain.tld/repo` with the credentials of `$USER` who is
+authenticated with `$PASSWD`. Finally, it `rsync`'s the file to the
+server before opening the local repository in your browser where you can
+adjust settings such as anonymous user access, and set pertinent
+repository details. Thereafter, you can add files with `fossil add`, and
+commit with `fossil ci -m 'commit message'` where Fossil, by default,
+will push to the `remote-url`. It's suggested you read the
+[Fossil documentation][documentation]; with a sane and consistent
+development model, the system is much more efficient and cohesive than
+`git`—so the learning curve is not steep at all.
+
+[documentation]: https://fossil-scm.org/home/doc/trunk/www/permutedindex.html
+
+*[Return to the top-level Fossil server article.](../)*
diff --git a/www/server/openbsd/index.md b/www/server/openbsd/index.md
new file mode 100644
index 0000000..ea4f95f
--- /dev/null
+++ b/www/server/openbsd/index.md
@@ -0,0 +1,6 @@
+# Debian/Ubuntu Specific Fossil Service Options
+
+- [Serving Fossil under OpenBSD’s `httpd` via FastCGI](./fastcgi.md)
+- [Running Fossil as a service on OpenBSD](./service.wiki)
+
+*[Return to the top-level Fossil server article.](../)*
diff --git a/www/server/openbsd/service.wiki b/www/server/openbsd/service.wiki
new file mode 100644
index 0000000..8729eb0
--- /dev/null
+++ b/www/server/openbsd/service.wiki
@@ -0,0 +1,118 @@
+<title>Serving via rc on OpenBSD</title>
+
+OpenBSD provides [https://man.openbsd.org/rc.subr.8|rc.subr(8)],
+a framework for writing [https://man.openbsd.org/rc.8|rc(8)] scripts.
+
+<h2>Creating the daemon</h2>
+
+Create the file /etc/rc.d/fossil with contents like the following.
+
+<pre>
+#!/bin/ksh
+daemon="/usr/local/bin/fossil" # fossil executable
+daemon_user="_fossil" # user to run fossil as
+daemon_flags="server /home/_fossil/example --repolist --port 8888" # fossil command
+
+. /etc/rc.d/rc.subr
+# pexp="$daemon server .*" # See below.
+rc_reload=NO # Unsupported by Fossil; 'rcctl reload fossil' kills the process.
+rc_bg=YES # Run in the background, since fossil serve does not daemonize itself
+rc_cmd $1
+</pre>
+
+<h3>pexp</h3>
+
+You may need to uncomment the "pexp=". rc.subr typically
+finds the daemon process based by matching the process name and argument list.
+Without the "pexp=" line, rc.subr would look for this exact command:
+
+<pre>
+/usr/local/bin/fossil server /home/_fossil/example --repolist --port 8888
+</pre>
+
+Depending on the arguments and their order, fossil may rewrite the arguments
+for display in the process listing ([https://man.openbsd.org/ps.1|ps(1)]),
+so rc.subr may fail to find the process through the default match. The example
+above does not get rewritten, but the same commands in a different order can
+be rewritten.
+For example, when I switch the order of the arguments in "daemon_flags",
+
+<pre>
+/usr/local/bin/fossil server --repolist --port 8888 /home/_fossil/example
+</pre>
+
+the process command is changed to this.
+
+<pre>
+/usr/local/bin/fossil server /home/_fossil/example /home/_fossil/example 8888 /home/_fossil/example
+</pre>
+
+The commented "pexp=" line instructs rc.subr to choose the process whose
+command and arguments text starts with this:
+
+<pre>
+/usr/local/bin/fossil server
+</pre>
+
+<h2>Enabling the daemon</h2>
+
+Once you have created /etc/rc.d/fossil, run these commands.
+
+<pre>
+rcctl enable fossil # add fossil to pkg_scripts in /etc/rc.conf.local
+rcctl start fossil # start the daemon now
+</pre>
+
+The daemon should now be running and set to start at boot.
+
+<h2>Multiple daemons</h2>
+
+You may want to serve multiple fossil instances with different options.
+For example,
+
+ * If different users own different repositories, you may want different users
+ to serve different repositories.
+ * You may want to serve different repositories on different ports so you can
+ control them differently with, for example, HTTP reverse proxies or
+ [https://man.openbsd.org/pf.4|pf(4)].
+
+To run multiple fossil daemons, create multiple files in /etc/rc.d, and
+enable each of them. Here are two approaches for creating
+the files in /etc/rc.d: Symbolic links and copies.
+
+<h3>Symbolic links</h3>
+
+Suppose you want to run one fossil daemon as user "user1" on port 8881
+and another as user "user2" on port 8882. Create the files with
+[https://man.openbsd.org/ln.1|ln(1)], and configure them to run different
+fossil commands.
+
+<pre>
+cd /etc/rc.d
+ln -s fossil fossil1
+ln -s fossil fossil2
+rcctl enable fossil1 fossil2
+rcctl set fossil1 user user1
+rcctl set fossil2 user user2
+rcctl set fossil1 flags 'server /home/user1/repo1.fossil --port 8881'
+rcctl set fossil2 flags 'server /home/user2/repo2.fossil --port 8882'
+rcctl start fossil1 fossil2
+</pre>
+
+<h3>Copies</h3>
+
+You may want to run fossil daemons that are too different to configure
+just with [https://man.openbsd.org/rcctl.8|rcctl(8)].
+In particular, you can't change the "pexp" with rcctl.
+
+If you want to run fossil commands that are more different,
+you may prefer to create separate files in /etc/rc.d.
+Replace "ln -s" above with "cp" to accomplish this.
+
+<pre>
+cp /etc/rc.d/fossil /etc/rc.d/fossil-user1
+cp /etc/rc.d/fossil /etc/rc.d/fossil-user2
+</pre>
+
+You can still use commands like "rcctl set fossil-user1 flags", but you
+can also edit the "/etc/rc.d/fossil-user1" file.
diff --git a/www/server/whyuseaserver.wiki b/www/server/whyuseaserver.wiki
new file mode 100644
index 0000000..cdd9823
--- /dev/null
+++ b/www/server/whyuseaserver.wiki
@@ -0,0 +1,95 @@
+<title>Benefits of a Fossil Server</title>
+
+<h2>No Server Required</h2>
+
+Fossil does not require a central server.
+Data sharing and synchronization can be entirely peer-to-peer.
+Fossil uses
+[https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type |
+conflict-free replicated data types]
+to ensure that (in the limit) all participating peers see the same content.
+
+<h2>But, a Server Can Be Useful</h2>
+
+Fossil does not require a server, but a server can be very useful.
+Here are a few reasons to set up a Fossil server for your project:
+
+ 1. <b>A server works as a complete project website.</b>
+
+ Fossil does more than just version control. It also supports
+ [../tickets.wiki|trouble-tickets],
+ [../wikitheory.wiki|wiki],
+ a [../chat.md|developer chat room], and a [../forum.wiki|forum].
+ The [../embeddeddoc.wiki|embedded documentation]
+ feature provides a great mechanism for providing project documentation.
+ The [../unvers.wiki|unversioned files] feature is a convenient way
+ to host builds and downloads on the project website.
+
+ 2. <b>A server gives developers a common point of rendezvous for
+ syncing their work.</b>
+
+ It is possible for developers to synchronize peer-to-peer but
+ that requires the developers coordinate the sync, which in turn
+ requires that the developers both want to sync at the same moment.
+ A server alleviates this time dependency by allowing each developer
+ to sync whenever it is convenient. For example, a developer may
+ choose to automatically sync
+ after each commit and before each update. Developers all stay
+ in sync with each other without having to interrupt each other
+ constantly to set up a peer-to-peer sync.
+
+ 3. <b>A server provides project leaders with up-to-date status.</b>
+
+ Project coordinators and BDFLs can click on a link or two at the
+ central Fossil server for a project and quickly tell what is
+ going on. They can do this from anywhere — even from their phones
+ — without needing to actually sync to the device they are using.
+
+ 4. <b>A server provides automatic off-site backups.</b>
+
+ A Fossil server is an automatic remote backup for all the work
+ going into a project. ([../backup.md | Within limits].)
+ You can even set up multiple servers at
+ multiple sites with automatic synchronization between them for
+ added redundancy. Such a setup means that no work is lost due
+ to a single machine failure.
+
+ 5. <b>A server consolidates [https://www.sqlite.org/howtocorrupt.html
+ | SQLite corruption risk mitigation] to a single point.</b>
+
+ The concerns in section 1 of that document assume you have direct
+ access to the central DB files, which isn't the case when the
+ server is remote and secure against tampering.
+
+ Section 2 is about file locking, which concerns disappear when Fossil's
+ on the other side of an HTTP boundary and your server is set up
+ properly.
+
+ Sections 3.1, 4 thru 6, and 8 apply to all Fossil configurations,
+ but setting up a server lets you address the risks
+ in a single place. Once a given commit is
+ sync'd to the server, you can be reasonably sure any client-side
+ corruption can be fixed with a fresh clone. Ultimately, this
+ is an argument for off-machine backups, which returns us to reason
+ #4 above.
+
+ Sections 3.2 and the entirety of section 7 are no concern with
+ Fossil at all, since it's primarily written by the creator and
+ primary maintainer of SQLite, so you can be certain Fossil doesn't
+ actively pursue coding strategies known to risk database corruption.
+
+ For another take on this topic, see the article
+ "[https://sqlite.org/useovernet.html | SQLite Over a Network,
+ Caveats and Considerations]". Fossil runs in rollback mode by
+ default per recommendation #3 at the end of that article, and a
+ Fossil server operates as a network proxy for the underlying
+ SQLite repository DB per recommendation #2. This <i>may</i> permit
+ you to safely switch it into WAL mode (<b>fossil rebuild --wal</b>)
+ depending on the underlying storage used by the server itself.
+
+ 6. <b>A server allows [../caps/ | Fossil's RBAC system] to work.</b>
+
+ The role-based access control (RBAC) system in Fossil only works
+ when the remote system is on the other side of an HTTP barrier.
+ ([../caps/#webonly | Details].) If you want its benefits, you need
+ a Fossil server setup of some kind.
diff --git a/www/server/windows/cgi-bin-perm.png b/www/server/windows/cgi-bin-perm.png
new file mode 100644
index 0000000..92ec090
--- /dev/null
+++ b/www/server/windows/cgi-bin-perm.png
Binary files differ
diff --git a/www/server/windows/cgi-exec-perm.png b/www/server/windows/cgi-exec-perm.png
new file mode 100644
index 0000000..5cfc2e3
--- /dev/null
+++ b/www/server/windows/cgi-exec-perm.png
Binary files differ
diff --git a/www/server/windows/cgi-install-iis.png b/www/server/windows/cgi-install-iis.png
new file mode 100644
index 0000000..ef6eb67
--- /dev/null
+++ b/www/server/windows/cgi-install-iis.png
Binary files differ
diff --git a/www/server/windows/cgi-script-map.png b/www/server/windows/cgi-script-map.png
new file mode 100644
index 0000000..bccc2fb
--- /dev/null
+++ b/www/server/windows/cgi-script-map.png
Binary files differ
diff --git a/www/server/windows/cgi.md b/www/server/windows/cgi.md
new file mode 100644
index 0000000..768c76f
--- /dev/null
+++ b/www/server/windows/cgi.md
@@ -0,0 +1,115 @@
+# Serving via IIS + CGI
+
+## This Is Not the Method You Are Looking For
+
+Setting up CGI service under IIS is surprisingly complicated compared to
+running Fossil as a CGI under most other operating systems. We recommend
+that you use the simpler [reverse proxying method](./iis.md) instead
+unless there is some compelling reason why that method cannot work for
+you, such as its dependence on non-stock IIS extensions. (Keep in mind
+that both extensions it requires are by Microsoft, not third parties!)
+
+Once you’ve got this scheme working, it gives the same benefits as those
+listed at the top of the linked-to document.
+
+There is a small benefit you get from using CGI over reverse proxying on
+other OSes, which is that the Fossil program only runs briefly in order
+to serve each HTTP hit. Once the request is done, that Fossil instance
+shuts back down, releasing all of its resources. You don’t need to keep
+a background Fossil HTTP server running full-time to provide CGI-based
+Fossil service.
+
+You lose a lot of that benefit on Windows:
+
+1. It only matters to start with on servers that are highly RAM
+ constrained. (Roughly &le; 128 MiB.) Our configuration steps below
+ assume you’re using the Windows and IIS GUIs, which have RAM
+ requirements well in excess of this, making Fossil’s resource
+ requirements a drop in the bucket next to them. On the [Azure
+ B1s][b1s] virtual machine I used to prepare these instructions, the
+ Windows Server Manager GUI kept filling the VM’s 1&nbsp;GiB of RAM
+ during feature installation and crashing. I had to upgrade the VM’s
+ RAM to 2&nbsp;GiB just to get useful work done!
+
+2. Process creation on Windows is [much more expensive][cp] than on the
+ other OSes Fossil runs on, so the benefits of firing up a Fossil
+ executable to process each HTTP request are partially swamped by the
+ overhead of doing so.
+
+Therefore, unless you’re willing to replace all of the GUI configuration
+steps below with command line equivalents, or shut the GUI down entirely
+after configuring IIS, CGI is a much less compelling option on Windows.
+
+**WARNING:** The following tutorial appears to fail with the current
+(2019-08-17) version of Fossil, [apparently][fbug] due to an inability
+of Fossil to detect that it’s being run in CGI mode.
+
+[b1s]: https://azure.microsoft.com/en-us/blog/introducing-b-series-our-new-burstable-vm-size/
+[cp]: https://stackoverflow.com/a/48244/142454
+[fbug]: https://fossil-scm.org/forum/forumpost/de18dc32c0
+
+
+## Install IIS with CGI Support
+
+The steps for this are identical to those for the [reverse proxying IIS
+setup](./iis.md#install) except that you need to enable CGI in the last
+step, since it isn’t installed by default. For Windows Server, the path
+is:
+
+![Install CGI in IIS](./cgi-install-iis.png)
+
+The path is similar on the consumer-focused versions of Windows, once
+you get to that last step.
+
+
+## Setup
+
+1. Install the Fossil executable to `c:\inetpub\wwwroot\bin` on the web
+ server. We can’t use an executable you might already have because IIS
+ runs under a separate user account, so we need to give that
+ executable special permissions, and that’s easiest to do under the
+ IIS tree:
+
+ ![IIS fossil.exe execute permission](./cgi-bin-perm.png)
+
+2. In IIS Manager (a.k.a. `INETMGR`) drill down into the Sites folder
+ in the left-side pane and right-click your web site’s
+ configuration. (e.g. “Default Web Site”)
+
+3. On that menu say “Add Virtual Directory.” Give it the alias “`cgi`”
+ and point it at a suitable directory, such as
+ “`c:\inetpub\wwwroot\cgi`”.
+
+4. Double-click the “Handler Mappings” icon, then in the right-side
+ pane, click “Add Script Map...” Apply the following settings:
+
+ ![IIS script map dialog](./cgi-script-map.png)
+
+ The Executable path must point to the path we set up in step 1, not
+ to some other `fossil.exe` you may have elsewhere on your system.
+ You will need to change the default “`*.dll`” filter in the Open
+ dialog to “`*.exe`” in order to see it when browsing via the “`...`”
+ button.
+
+5. Create a file called `repo.fslcgi` within the CGI directory you
+ chose in step 3, with a single line like this:
+
+ repository: c:\Users\SOMEONE\museum\repo.fossil
+
+ Give the actual path to the repository, of course.
+
+6. Up at the top level of IIS Manager, double-click the “ISAPI and CGI
+ Restrictions” icon, then click “Add...” in the right-side pane.
+ Give the script you just created permission to execute:
+
+ ![IIS CGI execute permission](./cgi-exec-perm.png)
+
+7. In the right-side pane, click “Restart” to apply this configuration,
+ then test it by visiting the newly-available URL in a browser:
+
+ http://localhost/cgi/repo.fslcgi
+
+For more complicated setups such as “directory” mode, see [the generic
+CGI instructions](../any/cgi.md).
+
+*[Return to the top-level Fossil server article.](../)*
diff --git a/www/server/windows/iis.md b/www/server/windows/iis.md
new file mode 100644
index 0000000..9107da1
--- /dev/null
+++ b/www/server/windows/iis.md
@@ -0,0 +1,134 @@
+# Serving via IIS
+
+## Why Bother?
+
+The first part of the scheme below sets Fossil up as an HTTP server, so
+you might be wondering why you wouldn’t just modify that to make it
+listen on all network interfaces on TCP port 80, so you can avoid the
+need for IIS entirely. For simple use cases, you can indeed do without
+IIS, but there are several use cases where adding it is helpful:
+
+1. Proxying Fossil with IIS lets you [add TLS encryption][tls], which
+ [Fossil does not currently speak](../../ssl.wiki) in its server role.
+
+2. The URL rewriting we do below allows Fossil to be part of a larger
+ site already being served with IIS.
+
+3. You can have a mixed-mode site, with Fossil acting as a powerful
+ dynamic content management service and IIS as a fast static content
+ server. The pure-Fossil alternative requires that you check all of
+ your static content into Fossil as versioned or unversioned
+ artifacts.
+
+This article shows how you can get any combination of those benefits by
+using IIS as a reverse proxy for `fossil server`.
+
+There are other ways to use IIS to serve Fossil, such as [via
+CGI](./cgi.md).
+
+
+## Background Fossil Service Setup
+
+You will need to have the Fossil HTTP server running in the background,
+serving some local repository, bound to localhost on a fixed
+high-numbered TCP port. For the purposes of testing, simply start it by
+hand in your command shell of choice:
+
+ fossil serve --port 9000 --localhost repo.fossil
+
+That command assumes you’ve got `fossil.exe` in your `%PATH%` and you’re
+in a directory holding `repo.fossil`. See [the platform-independent
+instructions](../any/none.md) for further details.
+
+For a more robust setup, we recommend that you [install Fossil as a
+Windows service](./service.md), which will allow Fossil to start at
+system boot, before anyone has logged in interactively.
+
+
+## <a id="install"></a>Install IIS
+
+IIS might not be installed in your system yet, so follow the path
+appropriate to your host OS. We’ve tested only the latest Microsoft
+OSes as of the time of this writing, but the basic process should be
+similar on older OSes.
+
+
+### Windows Server 2019
+
+1. Start “Server Manager”
+2. Tell it you want to “Add roles and features”
+3. Select “Role-based or feature-based installation”
+4. Select your local server
+5. In the Server Roles section, enable “Web Server (IIS)”
+
+### Windows
+
+1. Open Control Panel
+2. Go to “Programs”
+3. Select “Turn Windows features on or off” in the left-side pane
+4. In the “Windows Features” dialog, enable “Internet Information
+ Services”
+
+The default set of IIS features there will suffice for this tutorial,
+but you might want to enable additional features.
+
+
+## Setting up the Proxy
+
+The stock IIS setup doesn’t have reverse proxying features, but they’re
+easily added through extensions. You will need to install the
+[Application Request Routing][arr] and [URL Rewrite][ure] extensions. In
+my testing here, URL Rewrite showed up immediately after installing it,
+but I had to reboot the server to get ARR to show up. (Yay Windows.)
+
+You can install these things through the direct links above, or you can
+do it via the Web Platform Installer feature of IIS Manager (a.k.a.
+`INETMGR`).
+
+Set these extensions up in IIS Manager like so:
+
+1. Double-click the “Application Request Routing Cache” icon.
+
+2. Right-click in the window that results, and select “Server Proxy
+ Settings...”
+
+3. Check the “Enable Proxy” box in the dialog. Click the “Apply” text
+ in the right-side pane.
+
+4. Return to the top server-level configuration area of IIS Manager and
+ double-click the “URL Rewrite” icon. Alternately, you might find
+ “URL Rewrite” in the right-side pane from within the ARR settings.
+
+5. Right click in the window that results, and click “Add Rule(s)...”
+ Tell it you want a “Blank rule” under “Inbound rules”.
+
+6. In the dialog that results, create a new rule called “Fossil repo
+ proxy.” Set the “Pattern” to “`^(.*)$`” and “Rewrite URL” set to
+ “`http://localhost:9000/{R:1}`”. That tells it to take everything in
+ the path part of the URL and send it down to localhost:9000, where
+ `fossil server` is listening.
+
+7. Click “Apply” in the right-side pane, then get back to the top level
+ configuration for the server, and click “Restart” in that same pane.
+
+At this point, if you go to `http://localhost/` in your browser, you
+should see your Fossil repository’s web interface instead of the default
+IIS web site, as before you did all of the above.
+
+This is a very simple configuration. You can do more complicated and
+interesting things with this, such as redirecting only `/code` URLs to
+Fossil by setting the Pattern in step 6 to “`^/code(.*)$`”. (You would
+also need to pass `--baseurl http://example.com/code` in the `fossil
+server` command to make this work properly.) IIS would then directly
+serve all other URLs. You could also intermix ASP.NET applications in
+the URL scheme in this way.
+
+See the documentation on [URL Rewrite rules][urr] for more ideas.
+
+*[Return to the top-level Fossil server article.](../)*
+
+
+[arr]: https://www.iis.net/downloads/microsoft/application-request-routing
+[tls]: https://docs.microsoft.com/en-us/iis/manage/configuring-security/understanding-iis-url-authorization
+[ure]: https://www.iis.net/downloads/microsoft/url-rewrite
+[urr]: https://docs.microsoft.com/en-us/iis/extensions/url-rewrite-module/creating-rewrite-rules-for-the-url-rewrite-module
diff --git a/www/server/windows/index.md b/www/server/windows/index.md
new file mode 100644
index 0000000..a30b00a
--- /dev/null
+++ b/www/server/windows/index.md
@@ -0,0 +1,8 @@
+# Using Windows as a Fossil Server
+
+- [Fossil server command](./none.md)
+- [Fossil as CGI (IIS)](./iis.md)
+- [Fossil as a Service](./service.md)
+- [Using stunnel with Fossil on Windows](./stunnel.md)
+
+*[Return to the top-level Fossil server article.](../)*
diff --git a/www/server/windows/none.md b/www/server/windows/none.md
new file mode 100644
index 0000000..eab222d
--- /dev/null
+++ b/www/server/windows/none.md
@@ -0,0 +1,69 @@
+# Serving as a Standalone Server on Windows
+
+On Windows, this method works more or less identically to how it’s
+documented in [the generic instructions](../any/none.md).
+
+...but only while `fossil.exe` is actually running, which is the source
+of much trouble on Windows. This problem has two halves:
+
+
+## No App Startup Without Desktop
+
+The easy methods for starting a program in Windows at system start all
+require an interactive desktop. There is no *easy* way to start an arbitrary
+program on Windows at boot before anyone has logged in. In Unix
+terms, Windows has no simple equivalent to [the `/etc/rc.local` file][rcl].
+
+You can partially get around the first problem by setting your `fossil
+server` call up as one of the user’s interactive startup items. Windows
+10 has its own [idiosyncratic way of doing this][si10], and in older
+systems you have [several alternatives to this][si7]. Regardless of the
+actual mechanism, these will cause the Fossil standalone HTTP server to
+start on an *interactive desktop login* only. While you’re sitting at
+the Windows login screen, the Fossil server is *down*.
+
+[rcl]: http://nixdoc.net/man-pages/FreeBSD/man8/rc.local.8.html
+[si10]: https://www.tenforums.com/tutorials/2944-add-delete-enable-disable-startup-items-windows-10-a.html
+[si7]: https://www.wikihow.com/Change-Startup-Programs-in-Windows-7
+
+
+
+## No Simple Background Mode
+
+Windows also lacks a direct equivalent of the Bourne shell’s “`&`” control operator to
+run a program in the background, which you can give in Unix’s `rc.local`
+file, which is just a normal Bourne shell script.
+
+By “background,” I mean
+“not attached to any interactive user’s login session.” When the
+`rc.local` script exits in Unix, any program it backgrounded *stays
+running*. There is no simple and direct equivalent to this mechanism in
+Windows.
+
+If you set `fossil server` to run on interactive login, as above, it
+will shut right back down again when that user logs back out.
+
+With Windows 10, it’s especially problematic because you can no longer
+make the OS put off updates arbitrarily: your Fossil server will go down
+every time Windows Update decides it needs to reboot your computer, and
+then Fossil service will *stay* down until someone logs back into that
+machine interactively.
+
+
+## Better Solutions
+
+Because of these problems, we only recommend setting `fossil server` up
+on Windows this way when
+you’re a solo developer or you work in a small office where everyone
+arrives more or less at the same time each day, and everyone goes home
+about the same time each day, so that one user can keep the Fossil
+server up through the working day.
+
+If your needs go at all beyond this, you should expect proper “server”
+behavior, which you can get on Windows by [registering Fossil as a
+Windows service](./service.md), which solves the interactive startup and
+shutdown problems above, at a bit of complexity over the Startup Items
+method. You may also want to consider putting that service behind [an
+IIS front-end proxy](./iis.md) to add additional web serving features.
+
+*[Return to the top-level Fossil server article.](../)*
diff --git a/www/server/windows/service.md b/www/server/windows/service.md
new file mode 100644
index 0000000..e2cb3f2
--- /dev/null
+++ b/www/server/windows/service.md
@@ -0,0 +1,120 @@
+# Fossil as a Windows Service
+
+If you need Fossil to start automatically on Windows, it is suggested to install
+Fossil as a Windows Service.
+
+## Assumptions
+
+1. You have Administrative access to a Windows 2012r2 or above server.
+2. You have PowerShell 5.1 or above installed.
+
+## Place Fossil on Server
+
+However you obtained your copy of Fossil, it is recommended that you follow
+Windows conventions and place it within `\Program Files\FossilSCM`, the
+proper location for the official 64-bit binary.
+This way Fossil is at an expected location and you will have minimal issues with
+Windows interfering in your ability to run Fossil as a service. You will need
+Administrative rights to place fossil at the recommended location. If you will
+only be running Fossil as a service, you do not need to add this location to the
+path, though you may do so if you wish.
+
+## Installing Fossil as a Service
+
+Luckily the hard work to use Fossil as a Windows Service has been done by the
+Fossil team. We simply have to install it with the proper command line options.
+Fossil on Windows has a command `fossil winsrv` to allow installing Fossil as a
+service on Windows. This command is only documented on the windows executable
+of Fossil. You must also run the command as administrator for it to be
+successful.
+
+### Fossil winsrv Example
+
+The simplest form of the command is:
+
+```
+fossil winsrv create --repository D:/Path/to/Repo.fossil
+```
+
+This will create a windows service named 'Fossil-DSCM' running under the local
+system account and accessible on port 8080 by default. `fossil winsrv` can also
+start, stop, and delete the service. For all available options, please execute
+`fossil help winsrv` on a windows install of Fossil.
+
+If you wish to serve a directory of repositories, the `fossil winsrv` command
+requires a slightly different set of options vs. `fossil server`:
+
+```
+fossil winsrv create --repository D:/Path/to/Repos --repolist
+```
+
+### Choice of Directory Considerations
+
+When the Fossil server will be used at times that files may be locked
+during virus scanning, it is prudent to arrange that its directory used
+for temporary files is exempted from such scanning. Ordinarily, this
+will be a subdirectory named "fossil" in the temporary directory given
+by the Windows GetTempPath(...) API, [namely](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppathw#remarks)
+the value of the first existing environment variable from `%TMP%`, `%TEMP%`,
+`%USERPROFILE%`, and `%SystemRoot%`; you can look for their actual values in
+your system by accessing the `/test-env` webpage.
+Excluding this subdirectory will avoid certain rare failures where the
+fossil.exe process is unable to use the directory normally during a scan.
+
+### <a id='PowerShell'></a>Advanced service installation using PowerShell
+
+As great as `fossil winsrv` is, it does not have one to one reflection of all of
+the `fossil server` [options](/help/server). When you need to use some of
+the more advanced options, such as `--https`, `--skin`, or `--extroot`, you will
+need to use PowerShell to configure and install the Windows service.
+
+PowerShell provides the [New-Service](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/new-service?view=powershell-5.1)
+command, which we can use to install and configure Fossil as a service. The
+below should all be entered as a single line in an Administrative PowerShell
+console.
+
+```PowerShell
+New-Service -Name fossil -DisplayName fossil -BinaryPathName '"C:\Program Files\FossilSCM\fossil.exe" server --port 8080 --repolist "D:/Path/to/Repos"' -StartupType Automatic
+```
+
+Please note the use of forward slashes in the repolist path passed to Fossil.
+Windows will accept either back slashes or forward slashes in path names, but
+Fossil has a preference for forward slashes. The use of `--repolist` will make
+this a multiple repository server. If you want to serve only a single
+repository, then leave off the `--repolist` parameter and provide the full path
+to the proper repository file. Other options are listed in the
+[fossil server](/help/server) documentation.
+
+The service will be installed by default to use the Local Service account.
+Since Fossil only needs access to local files, this is fine and causes no
+issues. The service will not be running once installed. You will need to start
+it to proceed (the `-StartupType Automatic` parameter to `New-Service` will
+result in the service auto-starting on boot). This can be done by entering
+
+```PowerShell
+Start-Service -Name fossil
+```
+
+in the PowerShell console.
+
+Congratulations, you now have a base http accessible Fossil server running on
+Windows.
+
+### Removing the Windows Service
+
+If you want to remove the Fossil service, execute the following from an
+Administrative PowerShell or Command Prompt console:
+
+```
+sc.exe delete fossil
+```
+
+If you have Powershell version 6.0 or later, you can use:
+
+```PowerShell
+Remove-Service -Name fossil
+```
+
+with the same effect.
+
+*[Return to the top-level Fossil server article.](../)*
diff --git a/www/server/windows/stunnel.md b/www/server/windows/stunnel.md
new file mode 100644
index 0000000..5a7e8d1
--- /dev/null
+++ b/www/server/windows/stunnel.md
@@ -0,0 +1,156 @@
+# Using stunnel with Fossil on Windows
+
+While there are many ways to configure Fossil as a server using various web
+servers (Apache, IIS, nginx, etc.), this document will focus on setting up a
+minimal Fossil server using only Fossil's native [server
+capabilities](../any/none.md) and [stunnel](https://www.stunnel.org/)
+to provide a TLS proxy. It is recommended for public repositories to go to the
+extra step of configuring stunnel to provide a proper HTTPS setup.
+
+## Assumptions
+
+1. You have Administrative access to a Windows 2012r2 or above server.
+2. You have PowerShell 5.1 or above installed.
+3. You have acquired a certificate either from a Public CA or an Internal CA.
+
+## Configure Fossil Service for https
+
+Due to the need for the `--https` option for successfully using Fossil with
+stunnel, we will use [Advanced service installation using PowerShell](./service.md#PowerShell).
+We will need to change the command to install the Fossil Service to configure
+it properly for use with stunnel as an https proxy. Run the following:
+
+```PowerShell
+New-Service -Name fossil-secure -DisplayName fossil-secure -BinaryPathName '"C:\Program Files\FossilSCM\fossil.exe" server --localhost --port 9000 --https --repolist "D:/Path/to/Repos"' -StartupType Automatic
+```
+
+The use of `--localhost` means Fossil will only listen for traffic on the local
+host on the designated port - 9000 in this case - and will not respond to
+network traffic. Using `--https` will tell Fossil to generate HTTPS URLs rather
+than HTTP ones.
+
+`New-Service` does not automatically start a service on install, so you will
+need to enter the following to avoid rebooting the server:
+
+```PowerShell
+Start-Service -Name fossil-secure
+```
+
+To remove the service, run the following in a Powershell or cmd console:
+
+```
+sc.exe delete fossil
+```
+
+or (in a Powershell console)
+
+```PowerShell
+Remove-Service -Name fossil
+```
+
+if your version of Powershell is 6.0 or above.
+
+## Install stunnel 5.55
+
+Download stunnel from the [downloads](https://www.stunnel.org/downloads.html)
+page. Select the latest stunnel windows package (at the time of writing this is
+`stunnel-5.55-win64-installer.exe`). Execute the installer and make sure you
+install openSSL tools when you install stunnel. You will need this to convert
+your certificate from PFX to PEM format.
+
+Even though the installer says it is for win64, it installs stunnel by default
+to `\Program Files (x86)\stunnel`.
+
+## Get your certificate ready for Stunnel
+
+Whether you use a Public Certificate Authority or Internal Certificate
+Authority, the next step is exporting the certificate from Windows into a format
+useable by Stunnel.
+
+### Export Certificate from Windows
+
+If your certificate is installed via Windows Certificate Management, you will
+need to export the certificate into a usable format. You can do this either
+using the Windows Certificate Management Console, or PowerShell.
+
+#### Certificate Management Console
+
+Start `mmc.exe` as an Administrator. Select 'File>Add/Remove Snapin', select
+'Certificates' from the list, and click 'Add'. Select 'Computer Account',
+'Next', 'Local Computer', and then 'Finish'. In the Console Root, expand
+'Certificates', then 'Personal', and select 'Certificates'. In the middle pane
+find and select your certificate. Right click the certificate and select
+'All Tasks>Export'. You want to export as PFX the Private Key, include all
+certificates in the certification path, and use a password only to secure the
+file. Enter a path and file name to a working directory and complete the
+export.
+
+Continue with [Convert Certificate from PFX to PEM](#convert).
+
+#### PowerShell
+
+If you know the Friendly
+Name of the Certificate this is relatively easy. Since you need to export
+the private key as well, you must run the following from an Administrative
+PowerShell console.
+
+```PowerShell
+$passwd = ConvertTo-SecureString -string "yourpassword" -Force -AsPlainText
+
+Get-ChildItem Cert:\LocalMachine\My | Where{$_.FriendlyName -eq "FriendlyName"} |
+Export-PfxCertificate -FilePath fossil-scm.pfx -Password $passwd
+```
+
+You will now have your certificate stored as a PFX file.
+
+<a id="convert"></a>
+### Convert Certificate from PFX to PEM
+
+For this step you will need the openssl tools that were installed with stunnel.
+
+```PowerShell
+# Add stunnel\bin directory to path for this session.
+$env:PATH += ";${env:ProgramFiles(x86)}\stunnel\bin"
+# Export Private Key
+openssl.exe pkcs12 -in fossil-scm.pfx -out fossil-scm.key -nocerts -nodes
+# Export the Certificate
+openssl.exe pkcs12 -in fossil-scm.pfx -out fossil-scm.pem -nokeys
+```
+
+Now move `fossil-scm.key` and `fossil-scm.pem` to your stunnel config directory
+(by default this should be located at `\Program Files (x86)\stunne\config`).
+
+## stunnel Configuration
+
+Use the reverse proxy configuration given in the generic [Serving via
+stunnel document](../any/stunnel.md#proxy). On Windows, the
+`stunnel.conf` file is located at `\Program Files (x86)\stunnel\config`.
+
+You will need to modify it to point at the PEM and key files generated
+above.
+
+After completing the above configuration restart the stunnel service in Windows
+with the following:
+
+```PowerShell
+Restart-Service -Name stunnel
+```
+
+## Open up port 443 in the Windows Firewall
+
+The following instructions are for the [Windows Advanced
+Firewall](https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-firewall/windows-firewall-with-advanced-security).
+If you are using a different Firewall, please consult your Firewall
+documentation for how to open port 443 for inbound traffic.
+
+The following command should be entered all on one line.
+
+```PowerShell
+New-NetFirewallRule -DisplayName "Allow Fossil Inbound" -Description "Allow Fossil inbound on port 443 using Stunnel as TLS Proxy."
+ -Direction Inbound -Protocol TCP -LocalPort 443 -Action Allow -Program "C:\Program Files (x86)\Stunnel\bin\stunnel.exe"
+```
+
+You should now be able to access your new Fossil Server via HTTPS.
+
+
+*[Return to the top-level Fossil server article.](../)*