diff --git a/.github/workflows/book-gh-pages.yml b/.github/workflows/book-gh-pages.yml
index 96b3b6c3..c85315be 100644
--- a/.github/workflows/book-gh-pages.yml
+++ b/.github/workflows/book-gh-pages.yml
@@ -27,8 +27,8 @@ jobs:
- uses: cachix/install-nix-action@ba0dd844c9180cbf77aa72a116d6fbc515d0e87b # v27
- name: "Build documentation book"
run: |
- nix build '.#book' --print-build-logs
- cp -Lr --no-preserve=mode,ownership ./result/ ./book
+ nix build '.#docs' --print-build-logs
+ cp -Lr --no-preserve=mode,ownership ./result/share/doc/epnix/html ./book
- name: Setup Pages
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0
- name: Upload artifact
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 43c0ff54..8c6daaf2 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -31,8 +31,8 @@ formatting:
manpages:
stage: deploy
script:
- - nix build $NIX_BUILD_FLAGS '.#manpages'
- - cp -Lr ./result manpages
+ - nix build $NIX_BUILD_FLAGS '.#docs'
+ - cp -Lr ./result/share/man manpages
artifacts:
name: "manpages-$CI_COMMIT_REF_SLUG"
paths:
@@ -41,8 +41,8 @@ manpages:
pages:
stage: deploy
script:
- - nix build $NIX_BUILD_FLAGS '.#book'
- - cp -Lr ./result public
+ - nix build $NIX_BUILD_FLAGS '.#docs'
+ - cp -Lr ./result/share/doc/epnix/html public
artifacts:
name: "book"
paths:
diff --git a/.vale.ini b/.vale.ini
index 82822175..12e05b07 100644
--- a/.vale.ini
+++ b/.vale.ini
@@ -1,12 +1,12 @@
-StylesPath = doc/_vale
+StylesPath = docs/_vale
MinAlertLevel = suggestion
Vocab = EPNix
# External packages
Packages = alex, Google, Microsoft, proselint, RedHat, write-good
-# Only Markdown and .txt files; change to whatever you're using.
-[*.md]
+
+[*.{md,rst}]
# List of styles to load.
BasedOnStyles = alex, Google, Microsoft, proselint, RedHat, Vale, write-good
diff --git a/doc/.gitignore b/doc/.gitignore
deleted file mode 100644
index 081b57cd..00000000
--- a/doc/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-/src/options.md
-/src/packages.md
-book
-
-/.quarto/
-/_site
diff --git a/doc/_quarto.yml b/doc/_quarto.yml
deleted file mode 100644
index ff754c8f..00000000
--- a/doc/_quarto.yml
+++ /dev/null
@@ -1,97 +0,0 @@
-project:
- type: website
-
-website:
- title: "EPNix documentation"
- description: "Build, package EPICS IOCs and more using the Nix package manager"
- favicon: ./logo.svg
- repo-url: "https://github.com/epics-extensions/EPNix"
- repo-actions: [issue]
- page-navigation: true
- reader-mode: true
- navbar:
- search: true
- logo: ./logo.svg
- left:
- - text: "EPICS IOCs"
- file: ./ioc/introduction.md
- - text: "Packages"
- file: ./pkgs/introduction.md
- - text: "NixOS"
- file: ./nixos/introduction.md
- sidebar:
- - title: EPICS IOCs
- contents:
- - text: Introduction
- file: ./ioc/introduction.md
- - section: User Tutorials
- file: ./ioc/tutorials.md
- contents:
- - ./ioc/tutorials/pre-requisites.md
- - ./ioc/tutorials/streamdevice.md
- - ./ioc/tutorials/porting.md
- - ./ioc/tutorials/day-to-day-dev.md
- - ./ioc/tutorials/integration-tests.md
- - ./ioc/tutorials/creating-a-mock-server.md
- - ./ioc/tutorials/adding-options.md
- - file: ./ioc/guides.md
- contents:
- - ./ioc/guides/private-repo-setup.md
- - ./ioc/guides/flake-registry.md
- - ./ioc/guides/developing-modules.md
- - ./ioc/guides/pinning.md
- - ./ioc/guides/override-package.md
- - section: Testing
- file: ./ioc/guides/testing.md
- contents:
- - ./ioc/guides/testing/packaging-python-scripts.md
- - ./ioc/guides/testing/unit-tests.md
- - file: ./ioc/explanations.md
- - file: ./ioc/developer-tutorials.md
- - file: ./ioc/developer-guides.md
- contents:
- - ./ioc/developer-guides/packaging-modules.md
- - ./ioc/developer-guides/guidelines.md
- - ./ioc/developer-guides/packaging.md
- - ./ioc/developer-guides/glossary.md
- - file: ./ioc/references.md
- contents:
- - ./ioc/references/options.md
- - ./ioc/references/packages.md
- - ./ioc/faq.md
- - title: Packages
- contents:
- - text: Introduction
- file: ./pkgs/introduction.md
- - section: References
- contents:
- - ./pkgs/packages.md
- - title: NixOS
- contents:
- - title: NixOS
- file: ./nixos/introduction.md
- - section: Tutorials
- contents:
- - ./nixos/tutorials/archiver-appliance.md
- - section: User Guides
- contents:
- - ./nixos/guides/ca-gateway.md
- - ./nixos/guides/phoebus-alarm.md
- - ./nixos/guides/phoebus-save-and-restore.md
- - section: Explanations
- - section: References
- contents:
- - ./nixos/options.md
-
-format:
- html:
- theme:
- light: simplex
- dark: darkly
- highlight-style: gruvbox
- number-sections: false
- reference-location: margin
- citation-location: margin
- code-copy: true
- toc: true
- shift-heading-level-by: 1
diff --git a/doc/index.md b/doc/index.md
deleted file mode 100644
index d67fb159..00000000
--- a/doc/index.md
+++ /dev/null
@@ -1,68 +0,0 @@
----
-title: EPNix documentation
----
-
-![](./logo.svg){width=70% fig-align=center}
-
-# Introduction
-
-EPNix (pronunciation: like you are high on mushrooms) packages EPICS-related software using the [Nix] package manager.
-
-It's made of three parts:
-
-- the EPICS IOC framework
-- other EPICS-related packages
-- NixOS modules
-
-The EPICS IOC framework lets you package, deploy, and test EPICS IOCs using the Nix package manager, which provides several benefits.
-For more information, see the [EPICS IOCs introduction].
-
-EPNix also packages other EPICS-related tools, like procServ, Phoebus, etc.
-You can build them using Nix, and in the future download them pre-compiled, while having a strong guarantee that they will work as-is.
-For a list of all supported EPICS-related packages, see the [Packages list].
-
-EPNix also provides NixOS modules, which are instructions on how to configure various EPICS-related services on NixOS machines (for example the Phoebus alarm server).
-EPNix strives to have integration tests for each of those module.
-For more information, see the [NixOS modules introduction].
-
- [Nix]: https://nixos.org/guides/how-nix-works.html
- [EPICS IOCs introduction]: ./ioc/introduction.md
- [Packages list]: ./pkgs/packages.md
- [NixOS modules introduction]: ./nixos/introduction.md
-
-# Packaging policy
-
-As EPNix provides a package repository, packaging for example `epics-base`, `asyn`, `StreamDevice`, `procServ`, `phoebus`, etc., it needs to have a packaging policy.
-
-In its package repository, EPNix officially supports the latest upstream version.
-
-However, since EPNix is a git repository, you will be able, through Nix, to use a fixed version of EPNix, without being forced to upgrade your dependencies.
-
-```{=html}
-
-```
-
-## The epics-base package
-
-The epics-base package has no significant modification compared to the upstream version at [Launchpad].
-One goal of EPNix is to keep those modifications to a minimum, and upstream what's possible.
-
-# Release branches
-
-EPNix has a `master` branch,
-which is considered unstable,
-meaning breaking changes might happen without notice.
-
-EPNix also has release branches,
-such as `nixos-23.11`,
-tied to the nixpkgs release branches,
-where breaking changes are forbidden.
-
-Backporting changes to older release branches is done on a "best-effort" basis.
-
-------------------------------------------------------------------------
-
-This documentation follows the [Diátaxis] documentation framework.
-
- [Launchpad]: https://git.launchpad.net/epics-base
- [Diátaxis]: https://diataxis.fr/
diff --git a/doc/ioc/developer-guides.md b/doc/ioc/developer-guides.md
deleted file mode 100644
index 1bcdb330..00000000
--- a/doc/ioc/developer-guides.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Developer guides
----
-
-This section of the documentation book contains how-to guides[^1] for contributors to the EPNix project, related to building and deploying EPICS IOCs.
-
-[^1]:
diff --git a/doc/ioc/developer-guides/glossary.md b/doc/ioc/developer-guides/glossary.md
deleted file mode 100644
index f46edec5..00000000
--- a/doc/ioc/developer-guides/glossary.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Glossary
----
-
-TODO
diff --git a/doc/ioc/developer-guides/guidelines.md b/doc/ioc/developer-guides/guidelines.md
deleted file mode 100644
index aa6e3dce..00000000
--- a/doc/ioc/developer-guides/guidelines.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Guidelines
----
-
-TODO
diff --git a/doc/ioc/developer-guides/packaging-modules.md b/doc/ioc/developer-guides/packaging-modules.md
deleted file mode 100644
index ddecb6f1..00000000
--- a/doc/ioc/developer-guides/packaging-modules.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Packaging modules
----
-
-TODO
diff --git a/doc/ioc/developer-guides/packaging.md b/doc/ioc/developer-guides/packaging.md
deleted file mode 100644
index b4f88a7a..00000000
--- a/doc/ioc/developer-guides/packaging.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Packaging new software
----
-
-TODO
diff --git a/doc/ioc/developer-tutorials.md b/doc/ioc/developer-tutorials.md
deleted file mode 100644
index 13be9899..00000000
--- a/doc/ioc/developer-tutorials.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Developer Tutorials
----
-
-This section of the documentation book contains tutorials[^1] for contributors to the EPNix project, related to building and deploying EPICS IOCs.
-
-[^1]:
diff --git a/doc/ioc/explanations.md b/doc/ioc/explanations.md
deleted file mode 100644
index 9642641c..00000000
--- a/doc/ioc/explanations.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Explanations
----
-
-This section of the documentation book contains explanations[^1] of various concepts and patterns related to building and deploying EPICS IOCs with EPNix.
-
-[^1]:
diff --git a/doc/ioc/faq.md b/doc/ioc/faq.md
deleted file mode 100644
index 7d6b1b43..00000000
--- a/doc/ioc/faq.md
+++ /dev/null
@@ -1,49 +0,0 @@
----
-title: Frequently Asked Questions
----
-
-# I am getting weird Git errors about an unknown `-C` option
-
-You may be using a system with an old version of Git.
-You may install a recent version of Git for your user by running `nix-env -iA nixpkgs.git`{.bash}.
-
-# A file I created isn't found when I run `nix build`{.bash}
-
-If your top is a Git repository, you must `git add`{.bash} those files to make them recognized by Nix.
-
-# An App can't find a build product from another App
-
-EPNix enables parallel builds by default.
-These means that if App dependencies aren't specified, these Apps will compile in no particular order.
-Use `_DEPEND_DIRS += `{.makefile} in your top-level `Makefile`.
-
-# How do I version a whole EPNix top?
-
-Meaning, not versioning an App separate from the top.
-This might be justified if you don't intend to share an App in any other top.
-
-1. First, create a top and an App, as in the [StreamDevice tutorial].
-
-2. Make sure to add an exception for the `exampleApp` folder at the end of the top's `.gitignore` file:
-
-``` ini
-...
-# Applications and Support modules should be an EPNix dependency in flake.nix
-*App
-*Sup
-# You can add exceptions like this:
-# ---
-#!myCustomLocalApp
-!exampleApp
-```
-
-3. Then, version both the top and the App:
-
-``` bash
-git init
-git add -N .
-```
-
-4. Finally, in your `flake.nix`, you can remove any input and value in `epnix.applications.apps` that refers to this directory.
-
- [StreamDevice tutorial]: ./tutorials/streamdevice.md
diff --git a/doc/ioc/guides.md b/doc/ioc/guides.md
deleted file mode 100644
index c023ea30..00000000
--- a/doc/ioc/guides.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: User guides
----
-
-This section of the documentation book contains how-to guides[^1] for users of the EPNix project wanting to build EPICS IOCs.
-
-[^1]:
diff --git a/doc/ioc/guides/developing-modules.md b/doc/ioc/guides/developing-modules.md
deleted file mode 100644
index 79beb475..00000000
--- a/doc/ioc/guides/developing-modules.md
+++ /dev/null
@@ -1,74 +0,0 @@
----
-title: Developing support modules
----
-
-```{=html}
-
-```
-It can happen that one needs to hack on an EPICS support module, while also developing an App.
-This might be to develop and test the support module, or to patch and test the support module, etc.
-
-This is where Nix's reproducibility guarantees might seem to be in the way: dependencies are taken from the `/nix/store` instead of your local repository.
-You can add it as a flake input instead, but that requires to run `nix flake lock --update-input mySupport`{.bash} on each modification, etc.
-
-To bypass these constraints, there are several mechanisms that lets you to temporarily weaken these constraints for development purposes.
-
-# Packaging a starter module
-
-First, clone the EPNix repository, and package your support module.
-You can look at the [Packaging modules] guide, this doesn't even have to compile yet, but you need to specify the dependencies of your support module.
-
- [Packaging modules]: ../developer-guides/packaging-modules.md
-
-# Hacking on your module
-
-From the directory containing the source code of your support module, run:
-
-``` bash
-nix develop "/path/to/local/epnix#support/mySupport"
-# Then, inside the development shell
-dontUnpack=true
-genericBuild
-```
-
-This will put the result of your compilation under `outputs/out`.
-If you make modifications to your support module, run `buildPhase`{.bash} from the same development shell to recompile it.
-
-# Using it on your EPICS top
-
-Before trying to compile your top, make sure that your support module is included in the `support.modules` option of your EPNix top:
-
-``` nix
-support.modules = with pkgs.epnix.support; [ mySupport ];
-```
-
-Now that the support module is compiled and installed in a local directory, you can ask Nix to use it as is.
-This can be done by running this command from your EPICS top directory:
-
-``` bash
-nix develop \
- --override-input epnix '/path/to/local/epnix' \
- --redirect '/path/to/local/epnix#support/mySupport' '/path/to/mySupport/outputs/out'
-# Then, normal hacking on an EPICS top...
-```
-
-The `--override-input` option instructs Nix to use your local EPNix fork instead of the one hosted on GitHub.
-Use this option to override flake inputs.
-
-The `--redirect` option instructs Nix to use your local directory for your support module, instead of a module installed in the `/nix/store`.
-Use this option to override individual packages.
-
-------------------------------------------------------------------------
-
-With this setup, you can hack and compile your support module, and the changes will be directly visible to your top.
-This enables you to hack on both project at the same time, each on their own development shell.
-
-One question one may ask:
-
-> What's the difference between running the complex `nix develop`{.bash} command and just putting `/path/to/mySupport/outputs/out` into `RELEASE.local`?
-
-One thing that the complex `nix develop`{.bash} command does correctly, is replacing *everything* that would have been `/nix/store/...-mySupport../` into your development shell.
-This includes the `RELEASE.local` file, but this may not be the only thing:
-
-For example, if you're hacking on the `seq` support module, not only will it put the path to your local `seq` module into `RELEASE.local`, but it will also put some `seq` specific programs into your `$PATH`{.bash}, like the `snc`{.bash} utility.
-These programs will be those from your local build, not the ones coming from the EPNix repository.
diff --git a/doc/ioc/guides/flake-registry.md b/doc/ioc/guides/flake-registry.md
deleted file mode 100644
index 23483acd..00000000
--- a/doc/ioc/guides/flake-registry.md
+++ /dev/null
@@ -1,35 +0,0 @@
----
-title: Setting up the flake registry
----
-
-While developing with EPNix,
-it's possible you will end up typing `'github:epics-extensions/epnix'` quite often.
-
-It happens when you need to create a "top" template,
-or when you just want to have `epics-base` in your shell,
-and so on.
-
-This is tedious.
-
-Nix provides a way of shortening these URLs,
-by adding to the [Nix registry][]:
-
-``` bash
-nix registry add epnix 'github:epics-extensions/epnix'
-```
-
-Now, referring to `epnix` in Nix command-lines will be as if you referred to the full URL.
-For example, the develop command to have EPICS based installed outside of a top would be:
-
-``` bash
-nix develop epnix
-```
-
-If you want to initialize an EPNix top,
-you can run:
-
-``` bash
-nix flake new -t epnix my-top
-```
-
- [Nix registry]: https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-registry.html#description
diff --git a/doc/ioc/guides/override-package.md b/doc/ioc/guides/override-package.md
deleted file mode 100644
index 4609f0b2..00000000
--- a/doc/ioc/guides/override-package.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Override a support module
----
-
-TODO
diff --git a/doc/ioc/guides/pinning.md b/doc/ioc/guides/pinning.md
deleted file mode 100644
index b2dc7175..00000000
--- a/doc/ioc/guides/pinning.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Pin the version of a Nix dependency
----
-
-TODO
diff --git a/doc/ioc/guides/private-repo-setup.md b/doc/ioc/guides/private-repo-setup.md
deleted file mode 100644
index 685f3f9e..00000000
--- a/doc/ioc/guides/private-repo-setup.md
+++ /dev/null
@@ -1,119 +0,0 @@
----
-title: Private repository setup
----
-
-To avoid a great deal of confusion, it's best to configure your machine so that it can clone your private repositories unattended.
-This means that this command should succeed without asking for user input on the terminal:
-
-``` bash
-git clone 'ssh://git@your.gitlab.com/your/epicsApp.git'
-```
-
-But, asking for user input graphically is acceptable.
-
-The reason is that the Nix command-line tool often writes text on the terminal, and does so over the questions asked by programs like SSH or Git.
-If SSH or Git asks for a password on the terminal, you probably won't see it, and confusion will follow when the Nix command hangs.
-
-There's two main ways to configure your machine for this:
-
-- SSH keys
-- GitHub / GitLab tokens
-
-# SSH keys
-
-## Configuring the SSH key
-
-To setup SSH keys to use with your GitHub account, you can follow the [official documentation][github-ssh].
-
-To setup SSH keys to use with your GitLab account, you can follow the [official documentation][gitlab-ssh], and particularly look at these sections:
-
-- See if you have an existing SSH key pair
-- Generate an SSH key pair
-- Add an SSH key to your GitLab account
-- Verify that you can connect
-
- [github-ssh]: https://docs.github.com/en/authentication/connecting-to-github-with-ssh
- [gitlab-ssh]: https://docs.gitlab.com/ee/user/ssh.html
-
-## Configuring the ssh-agent
-
-If you have a GNOME installation, chances are you already have an ssh-agent installed and running.
-
-To check, open a *new* terminal, and look at the value of the `$SSH_AUTH_SOCK`{.bash} variable.
-
-If it returns nothing, install `gnome-keyring`, then log out and log in again.
-
-Then, reopen a new terminal, and add your configured SSH key like so:
-
-``` bash
-ssh-add 'path/to/key/id_ed25519'
-```
-
-Check that logging in to GitLab doesn't ask for user input on the terminal:
-
-``` bash
-ssh -T 'git@your.gitlab.com'
-# Welcome to GitLab, @user!
-```
-
-If it doesn't work, but you have `gnome-keyring-daemon` installed and running,
-you can add this line to your `~/.bashrc`:
-
-``` bash
-export SSH_AUTH_SOCK=/run/user/$UID/keyring/ssh
-```
-
-# GitHub / GitLab tokens
-
-GitHub and GitLab tokens offer a timed way of authenticating, suitable for either quick and dirty access, or for setting up services or scripts that need access to GitHub / GitLab repositories.
-In any case, tokens shouldn't be used for usual development.
-
-In GitLab, you can create tokens either per-user, per-group, or per-project.
-
-In GitHub, you can only create personal access tokens, but their usage can be restricted in an organization.
-
-Creating a GitHub token, or a Gitlab token for your user means that you can give it access to all projects and APIs that you have access to.
-This can be useful for your personal applications, or tools like [glab].
-
-In GitLab, you can create a token for given group or project so that it offers access to that specific group or project.
-These kinds of tokens should be preferred.
-
- [glab]: https://docs.gitlab.com/ee/integration/glab/
-
-## GitHub
-
-To create a GitHub token, follow the [official documentation][github-token]
-
- [github-token]: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token
-
-## GitLab
-
-```{=html}
-
-```
-To create a GitLab token, go to the user/group/project settings, under the "Access Tokens" section.
-Give your token a meaningful name, an expiration date, and for a group/project, select the appropriate role.
-
-If you only want your token to be able to clone repositories, you can just select the `read_repository` scope.
-Else refer to GitLab's official documentation by clicking "Learn more."
-
-After creating the personal access token, you can copy the token's value.
-Be careful, as this value won't be accessible after closing the page.
-
-If you want to clone any repository, the URL will need to be:
-
-``` bash
-https://gitlab-ci-token:${TOKEN}@your.gitlab.com"
-```
-
-As EPNix uses SSH flake inputs, you can use this command to instruct Git to rewrite GitLab URLs:
-
-``` bash
-git config --global url."https://gitlab-ci-token:${TOKEN}@your.gitlab.com".insteadOf "ssh://git@your.gitlab.com"
-```
-
-To check that your setup is working, run the following command:
-
-``` bash
-git clone 'ssh://git@your.gitlab.com/your/epicsApp.git'
-```
diff --git a/doc/ioc/guides/testing.md b/doc/ioc/guides/testing.md
deleted file mode 100644
index dadc5790..00000000
--- a/doc/ioc/guides/testing.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Testing
----
-
-TODO
diff --git a/doc/ioc/guides/testing/packaging-python-scripts.md b/doc/ioc/guides/testing/packaging-python-scripts.md
deleted file mode 100644
index 2bd5d2d1..00000000
--- a/doc/ioc/guides/testing/packaging-python-scripts.md
+++ /dev/null
@@ -1,188 +0,0 @@
----
-title: Packaging Python scripts
----
-
-```{=html}
-
-```
-As EPNix uses Nix, you can package Python scripts as helpers for your integration tests, by using the provided [infrastructure] of nixpkgs.
-In fact, you can package any program in any language, but this document focuses on Python scripts with [Poetry] for their simplicity and popularity.
-
- [infrastructure]: https://nixos.org/manual/nixpkgs/stable/#python
- [Poetry]: https://python-poetry.org/
-
-# Getting started
-
-We recommend using the Poetry package in your EPNix environment, through Nix, to use the same version as the one building the Python script.
-
-You can do this by adding this bit in your `flake.nix` file:
-
-``` nix
-epnix.devShell.packages = [
- { package = pkg.poetry; category = "development tools"; }
-];
-```
-
-Next, you can start your development shell with `nix develop`, go to the directory of your test, and create a new project with the command:
-
-``` bash
-poetry new
-```
-
-This will create a Python project under the `` directory.
-Under it, you will find a `pyproject.toml` where you can specify the dependencies of your script.
-For example, you can specify `modbus` to add the Python [modbus package], if you want to test modbus communication.
-You can remove the dependency on pytest if won't add unit tests to your Python script.
-
-To add an entry point to your Python code, you can use the `tool.poetry.scripts` section like so:
-
-``` toml
-[tool.poetry.scripts]
-my_python_script = "my_python_script:main"
-```
-
-This will add an executable named `my_python_script`{.bash} that will run the `main()`{.python} function of the `my_python_script`{.python} module.
-
-For more information on how to use poetry, please refer to the [Poetry documentation].
-
-Before packaging this script using Nix, it's important to generate the lock file, and to remember to re-generate it each time you change the `pyproject.toml` file.
-
-You can do this with the following command:
-
-``` bash
-poetry lock
-```
-
-Then, in your [integration test] file, you can package it like this:
-
-``` nix
-{ build, pkgs, ... }:
-
-let
- pythonScript = pkgs.poetry2nix.mkPoetryApplication {
- projectDir = ./path/to/my-python-script;
- };
-in
-pkgs.nixosTest {
- name = "myTest";
-
- # ...
-}
-```
-
-With this, you can use the `pythonScript` variable as you see fit.
-
- [modbus package]: https://pypi.org/project/modbus/
- [Poetry documentation]: https://python-poetry.org/docs/basic-usage/
- [integration test]: ../../tutorials/integration-tests.md
-
-# Example usage: As a one shot test script
-
-Using a packaged Python script instead of the provided `testScript` has several advantages.
-It can use dependencies provided by the community (like `modbus`, `systemd`, etc.), and you can make it run on the running virtual machine.
-
-Python script:
-
-``` python
-import subprocess
-
-from modbus.client import *
-
-
-def main():
- c = client(host='HOSTNAME')
- modbus_values = c.read(FC=3, ADR=10, LEN=8)
-
- for i in range(8):
- epics_value = subprocess.run(
- ["caget", "-t", "MyPV:" + i],
- capture_output=True,
- ).stdout.strip()
-
- assert modbus_values[i] == int(epics_value), "Wrong value provided by epics"
-```
-
-Nix test:
-
-``` nix
-{ build, pkgs, ... }:
-
-let
- pythonScript = pkgs.poetry2nix.mkPoetryApplication {
- projectDir = ./path/to/my-python-script;
- };
-in
-pkgs.nixosTest {
- name = "myTest";
-
- machine = {
- environment.systemPackages = [ pythonScript ];
-
- # ...
- };
-
- testScript = ''
- # ...
-
- my_python_script --my-flag --my-option=3
-
- # ...
- '';
-}
-```
-
-# Example usage: As a systemd service
-
-Using a Python script as a systemd service is useful for mocking devices.
-For more information, please see the [Creating a mocking server] guide.
-
-Python script:
-
-``` python
-import logging
-from logging import info
-
-
-def main():
- logging.basicConfig(level=logging.INFO)
-
- while True:
- info("doing things")
-
- # ...
-```
-
-Nix test:
-
-``` nix
-{ build, pkgs, ... }:
-
-let
- pythonScript = pkgs.poetry2nix.mkPoetryApplication {
- projectDir = ./path/to/my-python-script;
- };
-in
-pkgs.nixosTest {
- name = "myTest";
-
- machine = {
- systemd.services."my-python-service" = {
- wantedBy = [ "multi-user.target" ];
-
- serviceConfig.ExecStart = "${pythonScript}/bin/my_python_script";
- };
-
- # ...
- };
-
- testScript = ''
- # ...
-
- machine.wait_for_unit("my-python-service.service")
-
- # ...
- '';
-}
-```
-
- [Creating a mocking server]: ../../tutorials/creating-a-mock-server.md
diff --git a/doc/ioc/guides/testing/unit-tests.md b/doc/ioc/guides/testing/unit-tests.md
deleted file mode 100644
index b5d0032f..00000000
--- a/doc/ioc/guides/testing/unit-tests.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: Writing unit tests
----
-
-# EPICS unit tests primer
-
-TODO
-
-# EPICS unit test API
-
-# Example
diff --git a/doc/ioc/introduction.md b/doc/ioc/introduction.md
deleted file mode 100644
index 1da2c047..00000000
--- a/doc/ioc/introduction.md
+++ /dev/null
@@ -1,47 +0,0 @@
----
-title: EPICS IOCs
----
-
-# Introduction
-
-EPNix (pronunciation: like you are high on mushrooms) provides a way of building and packaging EPICS IOCs using the [Nix] package manager.
-
-By leveraging the Nix package manager, it provides several advantages compared to packaging EPICS the traditional way:
-
-Reproducibility:
-: Your development environment is the same as your coworker's development environment, which is the same as your production environment.
-
-Complete dependencies:
-: Your EPICS IOCs ship with the complete set of dependencies, which means you can to deploy your IOC without needing to install any dependency on the target machine (except for Nix itself).
-
-Dependency traceability:
-: The version of your dependencies are locked, updated manually, and traced in your `flake.lock` file.
- Combined with code versioning, you can build your project with the same environment years later, and you can roll back to any earlier version.
-
-Development shell:
-: Provides you with a set of tools adapted to your project, no matter what you have installed on your machine.
-
-Declarative configuration:
-: Define what you want in your IOC in a declarative and extendable manner.
-
-Integration tests:
-: Write tests using Python, by starting a virtual machine with your IOC running.
-
- [Nix]: https://nixos.org/guides/how-nix-works.html
-
-## Packaging policy
-
-To be able to specify your dependencies in your EPNix configuration, EPNix provides a package repository, packaging for example `epics-base`, `asyn`, `StreamDevice`, etc.
-
-In its package repository, EPNix officially supports the latest upstream version.
-
-However, since Nix "locks" your dependencies, this means you don't need to upgrade your dependencies if you don't want to.
-What this means in practice: your IOC repository uses the EPNix project repository at a fixed commit, like using a repository at a fixed point in time.
-Nix records this commit SHA in the `flake.lock` file, which should be checked out in your Git repository.
-
-### The epics-base package
-
-The epics-base package has no significant modification compared to the upstream version at [Launchpad].
-One goal of EPNix is to keep those modifications to a minimum, and upstream what's possible.
-
- [Launchpad]: https://git.launchpad.net/epics-base
diff --git a/doc/ioc/references.md b/doc/ioc/references.md
deleted file mode 100644
index cb56d640..00000000
--- a/doc/ioc/references.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: References
----
-
-This section of the book contains reference documentation[^1] related to building and deploying EPICS IOCs with EPNix.
-
-[^1]:
diff --git a/doc/ioc/tutorials.md b/doc/ioc/tutorials.md
deleted file mode 100644
index 39c569ff..00000000
--- a/doc/ioc/tutorials.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: User tutorials
----
-
-Tutorials[^1] for users of the EPNix project wanting to build and deploy EPICS IOCs.
-
-[^1]:
diff --git a/doc/ioc/tutorials/adding-options.md b/doc/ioc/tutorials/adding-options.md
deleted file mode 100644
index 60cbc4d8..00000000
--- a/doc/ioc/tutorials/adding-options.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adding configuration options
----
-
-TODO
diff --git a/doc/ioc/tutorials/creating-a-mock-server.md b/doc/ioc/tutorials/creating-a-mock-server.md
deleted file mode 100644
index 0bdc9738..00000000
--- a/doc/ioc/tutorials/creating-a-mock-server.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Creating a mocking server
----
-
-TODO
diff --git a/doc/ioc/tutorials/day-to-day-dev.md b/doc/ioc/tutorials/day-to-day-dev.md
deleted file mode 100644
index d1417baf..00000000
--- a/doc/ioc/tutorials/day-to-day-dev.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Day to day development
----
-
-TODO
diff --git a/doc/ioc/tutorials/integration-tests.md b/doc/ioc/tutorials/integration-tests.md
deleted file mode 100644
index 0db48a66..00000000
--- a/doc/ioc/tutorials/integration-tests.md
+++ /dev/null
@@ -1,121 +0,0 @@
----
-title: Integration tests
----
-
-# Writing the test
-
-Through the [NixOS testing framework], EPNix provides a way of specifying a machine configuration, and running a Python script that can do various kind of testing.
-
-If you created your IOC using the EPNix template, like suggested in the [StreamDevice tutorial], you will see a `checks/` directory.
-This directory should contain the integration tests you want to run.
-
-To add an integration test to EPNix, record it in your `flake.nix` under the `epnix.checks.files` option.
-
-For example, in the EPNix template, you will see in your `flake.nix` file:
-
-``` nix
-checks.files = [ ./checks/simple.nix ];
-```
-
-The `./checks/.nix` file should contain a NixOS test like so:
-
-``` nix
-{ build, pkgs, ... }:
-
-pkgs.nixosTest {
- name = "myTest";
-
- machine = {
- # Description of the NixOS machine...
- };
-
- testScript = ''
- # Python script that does the testing...
- '';
-}
-```
-
-This test will create a NixOS virtual machine from the given configuration, and run the test script.
-Note that the test script does *not* run on the virtual machine, but communicates with it.
-This is because the test script can start, shut down, or reboot the machine, and also because NixOS tests can also manage several virtual machines, not just one.
-
-For an overview of what you can input in the machine configuration, please refer to the [NixOS documentation].
-You can also read about the Python test script API [here][NixOS testing framework].
-
- [NixOS testing framework]: https://nixos.org/manual/nixos/stable/index.html#sec-nixos-tests
- [StreamDevice tutorial]: ./streamdevice.md
- [NixOS documentation]: https://nixos.org/manual/nixos/stable/index.html#sec-configuration-syntax
-
-# Starting your IOC through systemd
-
-We recommend starting your IOC through a systemd service, which you can describe in Nix like so:
-
-
-
-``` nix
-# Inside the `machine` attribute
-{
- systemd.services.my-ioc = {
- wantedBy = [ "multi-user.target" ];
- serviceConfig = {
- ExecStart = "${build}/iocBoot/iocexample/st.cmd";
- WorkingDirectory = "${build}/iocBoot/iocexample";
-
- # Makes the EPICS command-line not quit for 100 seconds, if it doesn't
- # receive anything on the standard input
- StandardInputText = "epicsThreadSleep(100)";
- };
- };
-
- # Provides the caget / caput / etc. commands to the test script
- environment.systemPackages = [ pkgs.epnix.epics-base ];
-}
-```
-
-You can view the list of options available for a NixOS machine [here].
-
-Then, you can write your test script.
-Note that the test script doesn't run directly on the machine, but communicates with the machine through the `machine` variable.
-
-An example of a testing script:
-
-``` python
-start_all()
-
-machine.wait_for_unit("default.target")
-machine.wait_for_unit("my-ioc.service")
-
-machine.wait_until_succeeds("caget stringin")
-machine.wait_until_succeeds("caget stringout")
-machine.fail("caget non-existing")
-
-with subtest("testing stringout"):
- def test_stringout(_) -> bool:
- machine.succeed("caput stringout 'hello'")
- status, _output = machine.execute("caget -t stringout | grep -qxF 'hello'")
-
- return status == 0
-
- retry(test_stringout)
-
- assert "hello" not in machine.succeed("caget -t stringin")
-```
-
-Note that the script extensively uses the `wait_until_succeeds` method and the `retry` function.
-This is because EPICS has few guarantees about whether it propagates changes immediately, and so it's better to encourage the use of retries, instead of hoping the timing lines up.
-
-If you would like to use a fully fledged python script on the machine, which can use Python dependencies like pyepics, please refer to the guide [Packaging Python scripts].
-
-You can find methods available on the `machine` variable and other particularities in the [NixOS tests documentation].
-
-You can also look at examples either in the EPNix repository under the [`checks` folder], or in nixpkgs under the [`nixos/tests` folder].
-
-```{=html}
-
-```
-
- [here]: https://search.nixos.org/options?channel=21.11&from=0&size=50&sort=alpha_asc&type=packages&query=systemd.services.
- [Packaging Python scripts]: ../guides/testing/packaging-python-scripts.md
- [NixOS tests documentation]: https://nixos.org/manual/nixos/stable/index.html#sec-nixos-tests
- [`checks` folder]: https://github.com/epics-extensions/epnix/tree/master/checks
- [`nixos/tests` folder]: https://github.com/NixOS/nixpkgs/tree/master/nixos/tests
diff --git a/doc/ioc/tutorials/porting.md b/doc/ioc/tutorials/porting.md
deleted file mode 100644
index d25efef1..00000000
--- a/doc/ioc/tutorials/porting.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Porting IOCs to EPNix
----
-
-TODO
diff --git a/doc/ioc/tutorials/pre-requisites.md b/doc/ioc/tutorials/pre-requisites.md
deleted file mode 100644
index 6ea7a9dd..00000000
--- a/doc/ioc/tutorials/pre-requisites.md
+++ /dev/null
@@ -1,76 +0,0 @@
----
-title: "Pre-requisites"
----
-
-The requirements for using EPNix are having curl, Nix, and Git installed,
-either in a Linux system,
-or in Windows' WSL2.
-Nix must be configured with "flakes" enabled.
-
-You *don't* need to have EPICS base installed globally,
-EPNix makes it available to you
-when you enter your top's development shell.
-
-Having a global EPICS base installation shouldn't pose any issue.
-
-# Installing Nix
-
-::: callout-warning
-If you use a Linux distribution with SELinux,
-be sure to turn it off.
-You can do this by adding the line `SELINUX=disabled` in `/etc/sysconfig/selinux` on distributions based on RedHat Enterprise Linux (RHEL) like CentOS, Rocky Linux, and so on.
-:::
-
-If you don't have Nix installed,
-first follow the [official instructions].
-Make sure to have the `xz` utility installed beforehand,
-often part of the `xzip` or `xz` package.
-
-Unless you use WSL2,
-use the multi-user installation,
-because it builds packages in an isolated environment.
-
- [official instructions]: https://nixos.org/download.html#download-nix
-
-# Enabling Nix flakes and the `nix`{.bash} command
-
-Because Nix flakes and the unified `nix` command are experimental features at the time of writing,
-you need to enable them in your `/etc/nix/nix.conf`.
-
-To enable this feature,
-add this line to your `/etc/nix/nix.conf`:
-
-``` ini
-experimental-features = nix-command flakes
-```
-
-If you have installed Nix in multi-user mode,
-then you have to restart the Nix daemon by running:
-
-``` bash
-systemctl restart nix-daemon.service
-```
-
-# Untracked files and Nix flakes
-
-One important thing with Nix flakes:
-when your flake is in a Git repository,
-Nix only considers files that Git tracks.
-
-For example,
-if your `flake.nix` is in a Git repository,
-and you create a file `foobar.txt`,
-you must run `git add [-N] foobar.txt`{.bash} to make Nix recognize it.
-
-This prevents copying build products into the Nix store.
-
-# Git version
-
-If you use an old system and see Git errors when using Nix,
-install a recent version of Git by running this:
-
-``` bash
-nix-env -iA nixpkgs.git
-```
-
-This command installs a recent version of Git for your current user.
diff --git a/doc/ioc/tutorials/streamdevice.md b/doc/ioc/tutorials/streamdevice.md
deleted file mode 100644
index 29441736..00000000
--- a/doc/ioc/tutorials/streamdevice.md
+++ /dev/null
@@ -1,294 +0,0 @@
----
-title: "Creating a StreamDevice IOC"
----
-
-In this tutorial,
-you're gonna learn how to create an EPICS IOC with EPNix
-that communicates with a power supply,
-using the [StreamDevice] support module.
-
- [StreamDevice]: https://paulscherrerinstitute.github.io/StreamDevice/
-
-# Pre-requisites
-
-Verify that you have all pre-requisites installed.
-If not,
-follow the [Pre-requisites] section.
-
- [Pre-requisites]: ./pre-requisites.md
-
-# Running the power supply simulator
-
-EPNix has a power supply simulator
-for you to test your IOC.
-
-To run it:
-
-``` bash
-nix run 'github:epics-extensions/epnix#psu-simulator'
-```
-
-For the rest of the tutorial,
-leave it running in a separate terminal.
-
-# Creating your top
-
-We can use these command to create an EPNix top:
-
-``` bash
-# Initialise an EPNix top
-nix flake new -t 'github:epics-extensions/epnix' my-top
-cd my-top
-
-# Enter the EPNix development shell, that has EPICS base installed in it.
-nix develop
-
-# Create your app and ioc boot folder
-makeBaseApp.pl -t ioc example
-makeBaseApp.pl -i -t ioc -p example -a linux-x86_64 Example
-
-# Create a git repository, and make sure all files are tracked
-git init
-git add .
-```
-
-After that,
-you can already check that your top build with:
-
-``` bash
-nix build -L
-```
-
-This `nix build`{.sh} command compiles your IOC,
-and all its dependencies.
-This makes the usual EPICS environment setup unneeded.
-
-If found in the official Nix cache server,
-Nix downloads packages from there
-instead of compiling them.
-
-This command puts a `./result` symbolic link in your current directory,
-containing the compilation result.
-
-# Adding StreamDevice to the EPNix environment
-
-Adding dependencies to the EPNix environment happen inside the `flake.nix` file.
-This file is the main entry point for specifying your build environment:
-most Nix commands used here read this file to work.
-
-For adding StreamDevice,
-change yours like so:
-
-``` {.diff filename="flake.nix"}
- # Add one of the supported modules here:
- # ---
-- #support.modules = with pkgs.epnix.support; [ StreamDevice ];
-+ support.modules = with pkgs.epnix.support; [ StreamDevice ];
-```
-
-Then,
-leave your EPNix development shell by running `exit`{.sh},
-and re-enter it with `nix develop`{.sh}.
-
-Because you modified the support modules,
-run `eregen-config`{.sh} to regenerate `configure/RELEASE.local`.
-
-With this,
-your development shell has StreamDevice available,
-and StreamDevice is also added in the `RELEASE.local` file.
-
-::: callout-tip
-As a rule,
-each time you edit the `flake.nix` file,
-leave and re-enter your development shell (`exit`{.sh} then `nix develop`{.sh}),
-and run `eregen-config`{.sh}.
-:::
-
-# Adding StreamDevice to your EPICS app
-
-To add StreamDevice to your app,
-make the following modifications:
-
-Change the `exampleApp/src/Makefile`
-so that your App knows the record types of StreamDevice and its dependencies.
-Also change that file so that it links to the StreamDevice library and its dependencies,
-during compilation.
-For example:
-
-``` {.makefile filename="exampleApp/src/Makefile"}
-# ...
-
-# Include dbd files from all support applications:
-example_DBD += calc.dbd
-example_DBD += asyn.dbd
-example_DBD += stream.dbd
-example_DBD += drvAsynIPPort.dbd
-
-# Add all the support libraries needed by this IOC
-example_LIBS += calc
-example_LIBS += asyn
-example_LIBS += stream
-
-# ...
-```
-
-Create the `exampleApp/Db/example.proto` file
-that has the definition of the protocol.
-This file tells StreamDevice what to send the power supply,
-and what to expect in return.
-
-``` {.perl filename="exampleApp/Db/example.proto"}
-Terminator = LF;
-
-getVoltage {
- out ":VOLT?"; in "%f";
-}
-
-setVoltage {
- out ":VOLT %f";
- @init { getVoltage; }
-}
-```
-
-Create the `exampleApp/Db/example.db` file.
-That file specifies the name, type, and properties of the Process Variables (PV)
-that EPICS exposes over the network.
-It also specifies how they relate to the functions written in the protocol file.
-
-``` {.perl filename="exampleApp/Db/example.db"}
-record(ai, "${PREFIX}VOLT-RB") {
- field(DTYP, "stream")
- field(INP, "@example.proto getVoltage ${PORT}")
-}
-
-record(ao, "${PREFIX}VOLT") {
- field(DTYP, "stream")
- field(OUT, "@example.proto setVoltage ${PORT}")
-}
-```
-
-Change `exampleApp/Db/Makefile`
-so that the EPICS build system installs `example.proto` and `example.db`:
-
-``` {.makefile filename="exampleApp/Db/Makefile"}
-# ...
-
-#----------------------------------------------------
-# Create and install (or just install) into /db
-# databases, templates, substitutions like this
-DB += example.db
-DB += example.proto
-
-# ...
-```
-
-Change your `st.cmd` file
-so that it knows where to load the protocol file,
-and how to connect to the remote power supply.
-
-``` {.csh filename="iocBoot/iocExample/st.cmd"}
-#!../../bin/linux-x86_64/example
-
-< envPaths
-
-## Register all support components
-dbLoadDatabase("${TOP}/dbd/example.dbd")
-example_registerRecordDeviceDriver(pdbbase)
-
-# Where to find the protocol files
-epicsEnvSet("STREAM_PROTOCOL_PATH", "${TOP}/db")
-# The TCP/IP address of the power supply
-drvAsynIPPortConfigure("PS1", "localhost:8727")
-
-## Load record instances
-dbLoadRecords("${TOP}/db/example.db", "PREFIX=, PORT=PS1")
-
-iocInit()
-```
-
-And run `chmod +x iocBoot/iocExample/st.cmd`
-so that you can run your command file as-is.
-
-You can test that your top builds by running:
-
-``` bash
-nix build -L
-```
-
-You will see that your IOC does not build.
-This is because we haven't told Git to track those newly added files,
-and so Nix ignores them too.
-
-Run `git add .`{.sh} for Git and Nix to track all files,
-and try a `nix build -L`{.sh} again.
-
-If everything goes right,
-you can examine your compiled top under `./result`.
-
-You can observe that:
-
-- the `example` app is installed under `bin/` and `bin/linux-x86_64`,
- and links to the correct libraries
-- `example.proto` and `example.db` are installed under `db/`
-- `example.dbd` is generated and installed under `dbd/`
-
-# Running your IOC
-
-To run your IOC,
-build it first with `nix build -L`{.sh},
-and change directory into the `./result/iocBoot/iocExample` folder.
-Then, run:
-
-``` bash
-./st.cmd
-```
-
-You should see the IOC starting and connecting to `localhost:8727`.
-
-# Recompiling with make
-
-Using `nix build`{.sh} to compile your IOC each time might feel slow.
-This is because Nix re-compiles your IOC from scratch each time.
-
-If you want a more "traditional" edit / compile / run workflow,
-you can place yourself in the development shell with `nix develop`{.sh},
-and use `make` from here.
-
-# Next steps
-
-More commands are available in the power supply simulator.
-To view them,
-close your IOC,
-and open a direct connection to the simulator:
-
-``` bash
-nc localhost 8727
-# or
-telnet localhost 8727
-```
-
-You can install the `nc` command through the `netcat` package,
-or you can install the `telnet` command through the `telnet` package,
-
-Either command opens a prompt
-where you can type `help` then press enter
-to view the available commands.
-
-Try to edit the protocol file and the database file
-to add those features to your IOC.
-
-For more information about how to write the StreamDevice protocol,
-have a look at the [Protocol Files] documentation.
-
-You might also be interested in reading [Setting up the flake registry]
-
- [Protocol Files]: https://paulscherrerinstitute.github.io/StreamDevice/protocol.html
- [Setting up the flake registry]: ../guides/flake-registry.md
-
-# Pitfalls
-
-Although EPNix tries to be close to a standard EPICS development,
-some differences might lead to confusion.
-You can find more information about this in the [FAQ].
-
- [FAQ]: ../faq.md
diff --git a/doc/nixos/guides/_pre-requisites.md b/doc/nixos/guides/_pre-requisites.md
deleted file mode 100644
index d13fe83a..00000000
--- a/doc/nixos/guides/_pre-requisites.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# Pre-requisites
-
-- Having a NixOS machine with a flake configuration.
-
-If you're not sure how to do this,
-you can follow the [Archiver Appliance tutorial],
-which is a good introduction on how to make a NixOS VM.
-
-If you have such a configuration,
-make sure that:
-
-- You have the `epnix` flake input
-- You have added `epnix` as an argument to your flake outputs
-- You have imported EPNix' NixOS module
-
-For example:
-
-``` {.diff filename="flake.nix"}
- {
- # ...
- inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
-+ inputs.epnix.url = "github:epics-extensions/EPNix/nixos-23.11";
-
- # ...
- outputs = {
- self,
- nixpkgs,
-+ epnix,
- }: {
- nixosConfigurations.nixos = nixpkgs.lib.nixosSystem {
- modules = [
-+ epnix.nixosModules.nixos
-
- # ...
- ];
- };
- };
- }
-```
-
- [Archiver Appliance tutorial]: ../tutorials/archiver-appliance.md
diff --git a/doc/nixos/guides/ca-gateway.md b/doc/nixos/guides/ca-gateway.md
deleted file mode 100644
index ede4ea15..00000000
--- a/doc/nixos/guides/ca-gateway.md
+++ /dev/null
@@ -1,195 +0,0 @@
----
-title: Channel Access gateway setup
----
-
-The Channel Access (CA) gateway is a program
-that acts as gateway,
-which enables client from a network to access IOCs on another network.
-
-Setting up a CA gateway also enables you
-to add extra access security rules on top of IOCs.
-
-For more details and documentation about the CA PV gateway,
-you can examine the [gateway main page].
-
- [gateway main page]: https://epics.anl.gov/extensions/gateway/
-
-{{< include _pre-requisites.md >}}
-
-# Enabling the gateway
-
-To enable the gateway,
-add this to your configuration:
-
-``` nix
-{
- services.ca-gateway = {
- enable = true;
- openFirewall = true;
- };
-}
-```
-
-This configuration starts the CA gateway in a `ca-gateway.service` systemd service.
-In this configuration,
-the gateway listens on all interface with a broadcast IP address,
-and forwards all Channel Access request.
-
-The `openFirewall` option opens the
-5064 TCP,
-5064 UDP,
-and 5065 UDP ports on all network interfaces.
-
-# Firewall on specific interfaces
-
-If you want to enable the firewall on specific interfaces,
-you can remove the `openFirewall` option
-and configure the firewall manually.
-
-You can also use the `cip` setting
-to specify where the gateway should listen
-for the CA server part.
-
-For example:
-
-``` nix
-{config, ...}: {
- services.ca-gateway = {
- enable = true;
- # Server side listen address
- # Let's say this IP address is on enp0s2
- settings.sip = ["10.0.2.1"];
-
- # Let's also say the enp0s1 interface is
- # where communication with "real" IOCs happen (client side)
-
- # openFirewall is left as false by default
- };
-
- networking.firewall = let
- gwSettings = config.services.ca-gateway.settings;
- in {
- interfaces = {
- # Open the firewall on the interface from the client side of the gateway,
- # this will be the side of the gateway listening
- # for replies to beacons and PV search requests
- "enp0s1".allowedUDPPorts = [5065];
-
- # Open the firewall on the interface from the server side of the gateway,
- # this will be the side of the gateway listening for Channel Access requests
- "enp0s2" = {
- # Use the value of the `sport` setting
- allowedTCPPorts = [gwSettings.sport];
- allowedUDPPorts = [gwSettings.sport];
- };
- };
-
- # Allow incoming UDP packets with *source* port 5064,
- # from the client side of the gateway.
- # This is needed to listen to CA broadcast responses
- extraCommands = ''
- ip46tables -A nixos-fw -p udp --sport 5064 -j nixos-fw-accept -i enp0s1
- '';
- };
-}
-```
-
-# Filtering IOCs
-
-By using the `cip` setting,
-you can filter which IOCs get exposed by the gateway.
-This is equivalent to setting the environment variable `EPICS_CA_ADDR_LIST`
-and setting `EPICS_CA_AUTO_ADDR_LIST=NO`.
-
-For example:
-
-``` nix
-{
- services.ca-gateway = {
- enable = true;
- # These IOCs get exposed by the gateway
- settings.cip = [
- "10.0.1.42"
- "10.0.1.69"
-
- # you can specify the port, too,
- # if your IOC listens on something other than 5064
- "10.0.1.237:5067"
-
- # domain names also work
- "myioc"
- ];
- };
-}
-```
-
-# Filtering process variables
-
-By using the `pvlist` setting,
-you can filter which PVs get exposed by the gateway.
-
-This option takes a file in the gateway `pvlist` format.
-See the [`GATEWAY.pvlist`] example on the ca-gateway repository.
-The list supports regular expressions (Perl style).
-
- [`GATEWAY.pvlist`]: https://github.com/epics-extensions/ca-gateway/blob/master/example/GATEWAY.pvlist
-
-## In the configuration
-
-For example:
-
-``` nix
-{pkgs, ...}: {
- services.ca-gateway = {
- enable = true;
- # These PVs get exposed by the gateway
- # This list implements an "allowlist":
- # DENY by default, some PVs explicitely ALLOW
- settings.pvlist = pkgs.writeText "gateway.pvlist" ''
- EVALUATION ORDER DENY, ALLOW
-
- .* DENY
-
- MY_PV1 ALLOW
- MY_PV2 ALLOW
-
- # Or:
-
- MY_PV[0-9]+ ALLOW
- '';
- };
-}
-```
-
-## In a separate file
-
-For long lists,
-it can be better
-to put it in a separate file.
-You can do this
-by adding a `gateway.pvlist` in the same directory as your configuration:
-
-``` {.perl filename="gateway.pvlist"}
-EVALUATION ORDER DENY, ALLOW
-
-.* DENY
-
-MY_PV1 ALLOW
-MY_PV2 ALLOW
-
-# Or:
-
-MY_PV[0-9]+ ALLOW
-```
-
-And in your configuration:
-
-``` nix
-{
- services.ca-gateway = {
- enable = true;
- # Make sure that the value is *not* quoted
- settings.pvlist = ./gateway.pvlist;
- };
-}
-```
diff --git a/doc/nixos/guides/phoebus-alarm.md b/doc/nixos/guides/phoebus-alarm.md
deleted file mode 100644
index 6c941b50..00000000
--- a/doc/nixos/guides/phoebus-alarm.md
+++ /dev/null
@@ -1,214 +0,0 @@
----
-title: Phoebus Alarm single server setup
----
-
-The Phoebus Alarm collection of services enables monitoring EPICS PVs,
-and report alarms in a server.
-Phoebus clients can then contact this server,
-to see a list of current alarms, earlier alarms, and so on.
-
-This guide focuses on installing and configuring these services on a single server.
-
-For more information about these services,
-examine the official documentation:
-
-- [Service Architecture]
-- [Alarm Server]
-- [the README of Alarm Server] for reference only, don't follow this guide on NixOS
-- [Alarm Logging Service]
-
-The Phoebus Alarm Logging Service can also be called the Phoebus Alarm Logger.
-
- [Service Architecture]: https://control-system-studio.readthedocs.io/en/latest/services_architecture.html
- [Alarm Server]: https://control-system-studio.readthedocs.io/en/latest/services/alarm-server/doc/index.html
- [the README of Alarm Server]: https://github.com/ControlSystemStudio/phoebus/blob/master/app/alarm/Readme.md
- [Alarm Logging Service]: https://control-system-studio.readthedocs.io/en/latest/services/alarm-logger/doc/index.html
-
-{{< include _pre-requisites.md >}}
-
-# Single server Phoebus Alarm setup
-
-To configure Phoebus Alarm, Phoebus Alarm Logger, Apache Kafka, and ElasticSearch on a single server,
-add this to your configuration,
-while taking care of replacing the IP address
-and Kafka's `clusterId`:
-
-``` nix
-{lib, pkgs, ...}: let
- # Replace this with your machine's external IP address
- # or DNS domain name
- ip = "192.168.1.42";
- kafkaListenSockAddr = "${ip}:9092";
- kafkaControllerListenSockAddr = "${ip}:9093";
-in {
- # The Phoebus Alarm server also automatically enables the Phoebus Alarm Logger
- services.phoebus-alarm-server = {
- enable = true;
- openFirewall = true;
- settings."org.phoebus.applications.alarm/server" = kafkaListenSockAddr;
- };
-
- services.phoebus-alarm-logger.settings."bootstrap.servers" = kafkaListenSockAddr;
-
- # Single-server Kafka setup
- services.apache-kafka = {
- enable = true;
- # Replace with a randomly generated uuid. You can get one by running:
- # nix shell 'nixpkgs#apacheKafka' -c kafka-storage.sh random-uuid
- clusterId = "xxxxxxxxxxxxxxxxxxxxxx";
- formatLogDirs = true;
- settings = {
- listeners = [
- "PLAINTEXT://${kafkaListenSockAddr}"
- "CONTROLLER://${kafkaControllerListenSockAddr}"
- ];
- # Adapt depending on your security constraints
- "listener.security.protocol.map" = [
- "PLAINTEXT:PLAINTEXT"
- "CONTROLLER:PLAINTEXT"
- ];
- "controller.quorum.voters" = [
- "1@${kafkaControllerListenSockAddr}"
- ];
- "controller.listener.names" = ["CONTROLLER"];
-
- "node.id" = 1;
- "process.roles" = ["broker" "controller"];
-
- "log.dirs" = ["/var/lib/apache-kafka"];
- "offsets.topic.replication.factor" = 1;
- "transaction.state.log.replication.factor" = 1;
- "transaction.state.log.min.isr" = 1;
- };
- };
-
- systemd.services.apache-kafka.unitConfig.StateDirectory = "apache-kafka";
-
- # Open kafka to the outside world
- networking.firewall.allowedTCPPorts = [9092];
-
- services.elasticsearch = {
- enable = true;
- package = pkgs.elasticsearch7;
- };
-
- # Elasticsearch, needed by Phoebus Alarm Logger, is not free software (SSPL | Elastic License).
- # To accept the license, add the code below:
- nixpkgs.config.allowUnfreePredicate = pkg:
- builtins.elem (lib.getName pkg) [
- "elasticsearch"
- ];
-}
-```
-
-From the Phoebus graphical client side,
-add this configuration:
-
-``` ini
-# For the Phoebus Alarm Server:
-# Replace the IP address with your server's IP address or DNS domain name
-org.phoebus.applications.alarm/server=192.168.1.42:9092
-
-# For the Phoebus Alarm Logger:
-# Replace the IP address again
-org.phoebus.applications.alarm.logging.ui/service_uri=http://192.168.1.42:8080
-```
-
-# Configuring topics
-
-The Phoebus Alarm system uses "topics" as a way of grouping alarms.
-These topics are the available roots of your alarm tree.
-You need to synchronize the topic names between:
-
-- Phoebus Alarm Server
-- Phoebus Alarm Logger
-- Phoebus graphical clients
-
-Changing the topic names in the Phoebus Alarm Server NixOS modules automatically creates them.
-
-::: callout-warning
-Currently, the Phoebus Alarm Server doesn't support several topics.
-:::
-
-For example,
-if you want to have the topic `Project`,
-add this configuration to the server:
-
-``` nix
-{config, lib, ...}: let
- topics = ["Project"];
-in {
- services.phoebus-alarm-server = {
- # ...
- settings = {
- # ...
- "org.phoebus.applications.alarm/config_names" = topics;
- };
- };
-
- services.phoebus-alarm-logger.settings.alarm_topics = topics;
-}
-```
-
-For the Phoebus graphical client,
-add this configuration:
-
-``` ini
-# config_name is only used in the Phoebus graphical client
-org.phoebus.applications.alarm/config_name = Project
-org.phoebus.applications.alarm/config_names = Project
-```
-
-# Configuring the address list
-
-If you want to limit the IOCs reachable by the Phoebus Alarm Server,
-use these option:
-
-``` nix
-{
- services.phoebus-alarm-server = {
- # ...
- settings = {
- # ...
-
- # The Phoebus Alarm Server will only have access to these IOCs
- "org.phoebus.pv.ca/addr_list" = ["192.168.1.5" "192.168.1.42"];
- "org.phoebus.pv.ca/auto_addr_list" = false;
- };
- };
-}
-```
-
-# Configuring email support
-
-To enable email support,
-set the `org.phoebus.email/mailport` setting.
-Here is a list of options you might want to set:
-
-``` nix
-{
- services.phoebus-alarm-server = {
- # ...
- settings = {
- # ...
-
- "org.phoebus.email/mailhost" = "smtp.my-company.org";
-
- # Optional:
-
- # 25 for plain SMTP
- "org.phoebus.email/mailport" = 25;
- # If authentication is needed:
- "org.phoebus.email/username" = "user";
- "org.phoebus.email/password" = "password";
- # Default address to be used for From:
- # if unspecified, then the last used "from" address is used
- "org.phoebus.email/from" = "Sender ";
- };
- };
-}
-```
-
-::: callout-warning
-Currently, Phoebus Alarm Server only supports plain SMTP.
-:::
diff --git a/doc/nixos/guides/phoebus-save-and-restore.md b/doc/nixos/guides/phoebus-save-and-restore.md
deleted file mode 100644
index f9a264f4..00000000
--- a/doc/nixos/guides/phoebus-save-and-restore.md
+++ /dev/null
@@ -1,50 +0,0 @@
----
-title: Phoebus Save-and-restore setup
----
-
-The Phoebus Save-and-restore service is used by clients
-to manage configuration and snapshots of PV values.
-These snapshots can then be used by clients for comparison or for restoring PVs.
-
-This guide focuses on installing and configuring the Save-and-Restore service on a single server.
-
-For more details and documentation about Phoebus Save-and-Restore,
-you can examine the [Save-and-restore official documentation].
-
- [Save-and-restore official documentation]: https://control-system-studio.readthedocs.io/en/latest/services/save-and-restore/doc/index.html
-
-{{< include _pre-requisites.md >}}
-
-# Enabling the Phoebus Save-and-restore service
-
-To enable the Phoebus Save-and-restore service,
-add this to your configuration:
-
-``` nix
-{lib, ...}: {
- services.phoebus-save-and-restore = {
- enable = true;
- openFirewall = true;
- };
-
- # Elasticsearch, needed by Phoebus Save-and-restore, is not free software (SSPL | Elastic License).
- # To accept the license, add the code below:
- nixpkgs.config.allowUnfreePredicate = pkg:
- builtins.elem (lib.getName pkg) [
- "elasticsearch"
- ];
-}
-```
-
-From the Phoebus graphical client side,
-add this configuration
-
-``` ini
-# Replace the IP address with your server's IP address or domain name
-org.phoebus.applications.saveandrestore/jmasar.service.url=http://192.168.1.42:8080
-```
-
-::: callout-warning
-URLs for future versions of Phoebus Save-and-restore will need to change to:
-`http://192.168.1.42:8080/save-restore`
-:::
diff --git a/doc/nixos/introduction.md b/doc/nixos/introduction.md
deleted file mode 100644
index ddc9d0a0..00000000
--- a/doc/nixos/introduction.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: NixOS
----
-
-# Introduction
diff --git a/doc/nixos/tutorials/archiver-appliance.md b/doc/nixos/tutorials/archiver-appliance.md
deleted file mode 100644
index 1e4a7e57..00000000
--- a/doc/nixos/tutorials/archiver-appliance.md
+++ /dev/null
@@ -1,359 +0,0 @@
----
-title: Creating an Archiver Appliance instance
----
-
-In this tutorial,
-we're going to see how to create a virtual machine that runs Archiver Appliance,
-under the NixOS Linux distribution.
-
-Installing Archiver Appliance on a physical machine is definitely possible,
-but this tutorial focuses on virtual machines for simplicity's sake.
-
-You will need:
-
-- A virtual machine,
-- and the [NixOS ISO file].
- Select the "Graphical ISO image."
-
- [NixOS ISO file]: https://nixos.org/download#download-nixos
-
-# Installing NixOS
-
-First things first,
-create your virtual machine,
-and select the ISO image that you downloaded.
-
-Then, start the virtual machine.
-
-From the booted virtual machine,
-you can follow the graphical installation process,
-and reboot once finished.
-
-You can select any desktop environment,
-or no desktop.
-This tutorial only uses the command-line.
-
-# Making your configuration a flake
-
-The installation process created the `/etc/nixos` directory in your VM.
-This directory describes the complete configuration of your machine.
-
-EPNix is a "Nix flake",
-which a way of managing Nix projects.
-Using Nix flakes also enables you to use Nix code outside of your repository,
-in a controlled manner.
-For more information,
-see the [Nix flake command manual] and the [Flake wiki page].
-
-To be able to import EPNix into you NixOS configuration,
-you first need to turn your NixOS configuration into a Nix flake.
-
-As root, place yourself in the `/etc/nixos` directory in your virtual machine.
-Create a `flake.nix` file under it,
-by running `nano flake.nix`.
-Fill the file with these lines:
-
-``` {.nix filename="flake.nix" code-line-numbers="true"}
-{
- description = "Configuration for running Archiver Appliance in a VM";
-
- inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
- inputs.epnix.url = "github:epics-extensions/EPNix/nixos-23.11";
-
- outputs = { self, nixpkgs, epnix }: {
- nixosConfigurations.nixos = nixpkgs.lib.nixosSystem {
- modules = [
- epnix.nixosModules.nixos
-
- ./configuration.nix
- ];
- };
- };
-}
-```
-
-Save and quit by typing {{< kbd Ctrl-x >}}, {{< kbd y >}}, and {{< kbd Enter >}},
-and run `nixos-rebuild test` to test your changes.
-
-Some explanations:
-
-You can see in the `flake.nix` file that the flake has 2 inputs:
-`nixpkgs` and `epnix`,
-lines 4--5.
-
-Having the `nixpkgs` input enables you to use code from [Nixpkgs].
-This is what enables you to use all those NixOS options,
-and every package installed on your machine now.
-For more information,
-you can read the [Nixpkgs preface]
-With the current configuration,
-we are only using code from Nixpkgs.
-
-Having the `epnix` input is what's going to enable you to use [packages from EPNix],
-such as Archiver Appliance.
-It also enables you to use [EPNix' extra NixOS options],
-such as the options configuring Tomcat, the systemd service, the `archappl` user and group, MariaDB, and so on.
-
- [Nix flake command manual]: https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake.html
- [Flake wiki page]: https://nixos.wiki/wiki/Flakes
- [Nixpkgs]: https://github.com/NixOS/nixpkgs
- [Nixpkgs preface]: https://nixos.org/manual/nixpkgs/stable/#preface
- [packages from EPNix]: ../../pkgs/packages.md
- [EPNix' extra NixOS options]: ../options.md
-
-# Configuring Archiver Appliance
-
-Now for the fun part,
-actually using those EPNix options to install and configure Archiver Appliance,
-and all its dependencies.
-
-Create and edit the file `archiver-appliance.nix` under `/etc/nixos`.
-For now, here are the contents:
-
-``` {.nix filename="archiver-appliance.nix"}
-{
- services.archiver-appliance.enable = true;
-}
-```
-
-In your `flake.nix`,
-import the newly created file by adding `./archiver-appliance.nix`,
-under `./configuration.nix`:
-
-``` diff
- modules = [
- epnix.nixosModules.nixos
-
- ./configuration.nix
-+ ./archiver-appliance.nix
- ];
-```
-
-If you try to test your changes by running `nixos-rebuild test`,
-you will see a helpful error message:
-
-::: sourceCode
-``` sourcecode
-error: The option `services.archiver-appliance.stores.lts.location'
- is used but not defined.
-(use '--show-trace' to show detailed location information)
-```
-:::
-
-This tells you that the `services.archiver-appliance.stores.lts.location` is mandatory,
-but we didn't set any value.
-
-To figure out what this option is about,
-you can examine the [options reference].
-
-The options reference gives a description for this option:
-
-> Backing directory containing the LTS.
-
-and an example:
-
-``` nix
-"/data/lts"
-```
-
-It tells us that you need to choose where the Long Term Store (LTS) is.
-See the "Architecture" section of the [Archiver Appliance Details] page for what the various stores are.
-
-Because this is a test VM,
-let's configure the LTS to a test location,
-like `/tmp/lts`.
-You will also need to configure the location of the Medium Term Store (MTS).
-
-Here's how to change `archiver-appliance.nix`:
-
-``` {.nix filename="archiver-appliance.nix"}
-{
- services.archiver-appliance.enable = true;
- services.archiver-appliance.stores.lts.location = "/tmp/lts";
- services.archiver-appliance.stores.mts.location = "/tmp/mts";
-}
-```
-
-If you don't want to repeat yourself,
-you can also change it like so:
-
-``` {.nix filename="archiver-appliance.nix"}
-{
- services.archiver-appliance = {
- enable = true;
- stores.lts.location = "/tmp/lts";
- stores.mts.location = "/tmp/mts";
- };
-}
-```
-
-And now,
-`nixos-rebuild test` should succeed:
-
-::: sourceCode
-``` sourcecode
-building the system configuration...
-activating the configuration...
-setting up /etc...
-reloading user units for admin...
-setting up tmpfiles
-reloading the following units: dbus.service
-the following new units were started: arch-lts-ArchiverStore.mount,
- arch-mts-ArchiverStore.mount, arch-sts-ArchiverStore.mount,
- mysql.service, tomcat.service
-```
-:::
-
-From the message,
-we can guess it started the Tomcat server running Archiver Appliance,
-the MySQL (in fact, MariaDB) server,
-and mounted some partitions.
-Fantastic!
-
-You can run the `systemctl list-units` command to see if any systemd unit failed.
-
-In the default configuration,
-Archiver Appliance and Tomcat are configured to output logs to journald.
-You can see those logs by running:
-
-``` bash
-journalctl -xeu tomcat.service
-```
-
-You can also see the MariaDB logs by running:
-
-``` bash
-journalctl -xeu mysql.service
-```
-
-::: callout-note
-Here are some details on what was done by EPNix' `services.archiver-appliance` NixOS module:
-
-- Creation of the Linux user and group `archappl`
-- Installation and configuration of MariaDB:
- - Creation of the `archappl` user,
- with UNIX socket authentication
- - Creation of the Archiver Appliance database
- - Creation of the [various tables] in that database
- - Giving access rights to this database for the `archappl` user
-- Installation and configuration of Tomcat:
- - Installation of the WAR files of Archiver Appliance
- - Installation of the MariaDB connector and its dependencies
- - Configuring the MariaDB connector to authenticate to the database
- - Logging configuration to `journald`
-- Configuring mounts so that:
- - `/arch/lts` and `/arch/mts` are bind mounts to the configured locations,
- with some added security options,
- such as `nodev` and `noexec`
- - Mounting `/arch/sts` as a new `tmpfs`
-:::
-
-Tomcat runs by default under port 8080,
-and NixOS has a firewall enabled by default.
-
-Change your `archiver-appliance.nix`:
-
-``` {.nix filename="archiver-appliance.nix"}
-{
- services.archiver-appliance = {
- enable = true;
- stores.lts.location = "/tmp/lts";
- stores.mts.location = "/tmp/mts";
-
- # New option:
- openFirewall = true;
- };
-}
-```
-
-and run `nixos-rebuild test`.
-It will restart `firewall.service`,
-but configured to allow incoming connection on port 8080.
-
-Check the IP address of your VM with `ip a`,
-and open a browser to `http://:8080/mgmt/ui/index.html`.
-
-Finally,
-run `nixos-rebuild switch` to confirm your changes.
-This will apply your changes for the next reboot,
-by adding a new boot entry,
-enabling you to go back to a previous configuration.
-
-You have now configured Archiver Appliance on NixOS.
-
- [options reference]: ../options.md
- [Archiver Appliance Details]: https://slacmshankar.github.io/epicsarchiver_docs/details.html
- [various tables]: https://github.com/slacmshankar/epicsarchiverap/blob/master/src/main/org/epics/archiverappliance/config/persistence/archappl_mysql.sql
-
-# Next steps
-
-This VM configuration has some problems:
-
-- It stores the LTS and MTS in `/tmp`,
- which by default is cleaned on reboot
-- The size of the Short Term Store (STS) isn't configured
-- Both "management" and "retrieval" URLs are accessible without authentication
-
-The following sections are some pointers to fix these issues.
-
-## Configuring partitions
-
-If you want to change the location of the LST or MTS,
-you can change the value of the corresponding options:
-
-- `services.archiver-appliance.stores.lts.location`
-- `services.archiver-appliance.stores.mts.location`
-
-But these values won't mean much if the configured directories are not backed by the appropriate hardware.
-
-As an example given by the [Archiver Appliance Details] page,
-section "Architecture",
-we can have the LTS backed by a NAS or SAN,
-and the MTS backed by SSD or SAS storage.
-
-The way to do that is to configure the `fileSystems` NixOS option.
-See the [File Systems NixOS documentation] for more information.
-
- [Archiver Appliance Details]: https://slacmshankar.github.io/epicsarchiver_docs/details.html
- [File Systems NixOS documentation]: https://nixos.org/manual/nixos/stable/#ch-file-systems
-
-## Size of the short term store
-
-To configure the size of the short term store,
-use the `services.archiver-appliance.stores.sts.size` option.
-
-For example:
-
-``` {.nix filename="archiver-appliance.nix"}
-{
- services.archiver-appliance = {
- enable = true;
- stores.lts.location = "/tmp/lts";
- stores.mts.location = "/tmp/mts";
-
- openFirewall = true;
-
- # New option:
- stores.sts.size = "20g";
- };
-}
-```
-
-See the [`sts.size` option] in the reference for a more in-depth description.
-
- [`sts.size` option]: ../options.md#services.archiver-appliance.stores.sts.size
-
-## Restricting access
-
-Allowing access to `mgmt` URLs to anyone can be dangerous,
-because it allows anyone to delete and archive PVs.
-
-To restrict access,
-you can close the firewall and put an nginx server in front.
-
-You can configure the nginx server to disallow access to the URLs you want.
-You can also configure nginx to require authentication.
-
-```{=html}
-
-```
diff --git a/doc/pkgs/introduction.md b/doc/pkgs/introduction.md
deleted file mode 100644
index 7baabb9d..00000000
--- a/doc/pkgs/introduction.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Packages
----
-
-# Introduction
diff --git a/docs/.gitignore b/docs/.gitignore
new file mode 100644
index 00000000..e35d8850
--- /dev/null
+++ b/docs/.gitignore
@@ -0,0 +1 @@
+_build
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 00000000..d4bb2cbb
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS ?=
+SPHINXBUILD ?= sphinx-build
+SOURCEDIR = .
+BUILDDIR = _build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/_ext/pygments_styles.py b/docs/_ext/pygments_styles.py
new file mode 100644
index 00000000..76e9ba1e
--- /dev/null
+++ b/docs/_ext/pygments_styles.py
@@ -0,0 +1,197 @@
+"""Custom pygments highlight styles, based on Nord.
+
+Modified so that:
+
+- Nord has a light theme
+- It is AA readable
+- It uses the main blue colors of the EPNix logo
+"""
+
+from pygments.style import Style
+from pygments.token import (
+ Comment,
+ Error,
+ Generic,
+ Keyword,
+ Name,
+ Number,
+ Operator,
+ Punctuation,
+ String,
+ Text,
+ Token,
+ Whitespace,
+)
+
+# Nord palette
+darker_nord0 = "#242933"
+nord0 = "#2e3440"
+nord1 = "#3b4252"
+nord2 = "#434c5e"
+nord3 = "#4c566a"
+nord4 = "#d8dee9"
+nord5 = "#e5e9f0"
+nord6 = "#eceff4"
+nord7 = "#8fbcbb" # blue green
+nord8 = "#88c0d0" # light blue
+nord9 = "#81a1c1" # blue
+nord10 = "#5e81ac" # deep blue
+nord11 = "#bf616a" # red
+nord12 = "#d08770" # orange
+nord13 = "#ebcb8b" # yellow
+nord14 = "#a3be8c" # green
+nord15 = "#b48ead" # purple
+
+# EPNix palette
+epnix_blue0 = "#0b1924"
+epnix_blue1 = "#18334b"
+epnix_blue2 = "#5277c3"
+epnix_blue3 = "#415e9a"
+epnix_blue4 = "#7ebae4"
+
+epnix_dark_background = "#1a1c1e" # from the furo theme
+epnix_light_background = "#f8f9fb" # from the furo theme
+
+
+class EpnixNordLight(Style):
+ name = "epnix-nord-light"
+
+ _green = "#577140" # nord11 with lightness darkened by 0.3
+ _red = "#a9444e" # nord11 with lightness darkened by 0.1
+ _orange = "#a45036" # nord12 with lightness darkened by 0.2
+ _yellow = _orange # Can't get the yellow to work
+ _purple = "#84587c" # nord15 with lightness darkened by 0.2
+
+ _normal_text = nord0
+ _faded_text = nord3
+ _background = epnix_light_background
+ _highlight_background = nord4
+ _comment = "#3f6e75" # stolen from the xcode style
+ _keyword = epnix_blue1
+ _function = epnix_blue3
+ _string = _green
+
+ line_number_color = _faded_text
+ line_number_background_color = _background
+ line_number_special_color = _background
+ line_number_special_background_color = _faded_text
+
+ background_color = _background
+ highlight_color = _highlight_background
+
+ styles = {
+ Token: _normal_text,
+ Whitespace: _normal_text,
+ Punctuation: _normal_text,
+ Comment: f"italic {_comment}",
+ Comment.Preproc: _keyword,
+ Keyword: f"bold {_keyword}",
+ Keyword.Pseudo: f"nobold {_keyword}",
+ Keyword.Type: f"nobold {_keyword}",
+ Operator: f"bold {_keyword}",
+ Operator.Word: f"bold {_keyword}",
+ Name: _normal_text,
+ Name.Builtin: _keyword,
+ Name.Function: _function,
+ Name.Class: _function,
+ Name.Namespace: _function,
+ Name.Exception: _red,
+ Name.Variable: _normal_text,
+ Name.Constant: _function,
+ Name.Entity: _orange,
+ Name.Attribute: _function,
+ Name.Tag: _keyword,
+ Name.Decorator: _orange,
+ String: _string,
+ String.Doc: _comment,
+ String.Interpol: _string,
+ String.Escape: _yellow,
+ String.Regex: _yellow,
+ String.Symbol: _yellow, # modified, looks better with Nix
+ String.Other: _string,
+ Number: _purple,
+ Generic.Heading: f"bold {_function}",
+ Generic.Subheading: f"bold {_function}",
+ Generic.Deleted: _red,
+ Generic.Inserted: _green,
+ Generic.Error: _red,
+ Generic.Emph: "italic",
+ Generic.Strong: "bold",
+ Generic.Prompt: f"bold {_comment}", # modified
+ Generic.Output: _normal_text,
+ Generic.Traceback: _red,
+ Error: _red,
+ Text: _normal_text,
+ }
+
+
+class EpnixNordDarker(Style):
+ """Based on "nord-darker"."""
+
+ _green = nord14
+ _red = "#ce858c" # nord11 with lightness lightened by 0.1
+ _orange = nord12
+ _yellow = nord13
+ _purple = nord15
+
+ _normal_text = nord6
+ _faded_text = nord4
+ _background = epnix_dark_background # modified
+ _highlight_background = nord1
+ _comment = nord9 # modified
+ _keyword = epnix_blue4 # modified
+ _function = nord7 # modified
+ _string = _green
+
+ line_number_color = _faded_text
+ line_number_background_color = _background
+ line_number_special_color = _background
+ line_number_special_background_color = _faded_text
+
+ background_color = _background
+ highlight_color = _highlight_background
+
+ styles = {
+ Token: _normal_text,
+ Whitespace: _normal_text,
+ Punctuation: _normal_text,
+ Comment: f"italic {_comment}",
+ Comment.Preproc: _keyword,
+ Keyword: f"bold {_keyword}",
+ Keyword.Pseudo: f"nobold {_keyword}",
+ Keyword.Type: f"nobold {_keyword}",
+ Operator: f"bold {_keyword}",
+ Operator.Word: f"bold {_keyword}",
+ Name: _normal_text,
+ Name.Builtin: _keyword,
+ Name.Function: _function,
+ Name.Class: _function,
+ Name.Namespace: _function,
+ Name.Exception: _red,
+ Name.Variable: _normal_text,
+ Name.Constant: _function,
+ Name.Entity: _orange,
+ Name.Attribute: _function,
+ Name.Tag: _keyword,
+ Name.Decorator: _orange,
+ String: _string,
+ String.Doc: _comment,
+ String.Interpol: _string,
+ String.Escape: _yellow,
+ String.Regex: _yellow,
+ String.Symbol: _yellow, # modified, looks better with Nix
+ String.Other: _string,
+ Number: _purple,
+ Generic.Heading: f"bold {_function}",
+ Generic.Subheading: f"bold {_function}",
+ Generic.Deleted: _red,
+ Generic.Inserted: _green,
+ Generic.Error: _red,
+ Generic.Emph: "italic",
+ Generic.Strong: "bold",
+ Generic.Prompt: f"bold {_comment}", # modified
+ Generic.Output: _normal_text,
+ Generic.Traceback: _red,
+ Error: _red,
+ Text: _normal_text,
+ }
diff --git a/doc/_vale/Google/vocab.txt b/docs/_static/.gitkeep
similarity index 100%
rename from doc/_vale/Google/vocab.txt
rename to docs/_static/.gitkeep
diff --git a/docs/_templates/.gitkeep b/docs/_templates/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/doc/_vale/Google/AMPM.yml b/docs/_vale/Google/AMPM.yml
similarity index 100%
rename from doc/_vale/Google/AMPM.yml
rename to docs/_vale/Google/AMPM.yml
diff --git a/doc/_vale/Google/Acronyms.yml b/docs/_vale/Google/Acronyms.yml
similarity index 100%
rename from doc/_vale/Google/Acronyms.yml
rename to docs/_vale/Google/Acronyms.yml
diff --git a/doc/_vale/Google/Colons.yml b/docs/_vale/Google/Colons.yml
similarity index 100%
rename from doc/_vale/Google/Colons.yml
rename to docs/_vale/Google/Colons.yml
diff --git a/doc/_vale/Google/Contractions.yml b/docs/_vale/Google/Contractions.yml
similarity index 100%
rename from doc/_vale/Google/Contractions.yml
rename to docs/_vale/Google/Contractions.yml
diff --git a/doc/_vale/Google/DateFormat.yml b/docs/_vale/Google/DateFormat.yml
similarity index 100%
rename from doc/_vale/Google/DateFormat.yml
rename to docs/_vale/Google/DateFormat.yml
diff --git a/doc/_vale/Google/Ellipses.yml b/docs/_vale/Google/Ellipses.yml
similarity index 100%
rename from doc/_vale/Google/Ellipses.yml
rename to docs/_vale/Google/Ellipses.yml
diff --git a/doc/_vale/Google/EmDash.yml b/docs/_vale/Google/EmDash.yml
similarity index 100%
rename from doc/_vale/Google/EmDash.yml
rename to docs/_vale/Google/EmDash.yml
diff --git a/doc/_vale/Google/EnDash.yml b/docs/_vale/Google/EnDash.yml
similarity index 100%
rename from doc/_vale/Google/EnDash.yml
rename to docs/_vale/Google/EnDash.yml
diff --git a/doc/_vale/Google/Exclamation.yml b/docs/_vale/Google/Exclamation.yml
similarity index 100%
rename from doc/_vale/Google/Exclamation.yml
rename to docs/_vale/Google/Exclamation.yml
diff --git a/doc/_vale/Google/FirstPerson.yml b/docs/_vale/Google/FirstPerson.yml
similarity index 100%
rename from doc/_vale/Google/FirstPerson.yml
rename to docs/_vale/Google/FirstPerson.yml
diff --git a/doc/_vale/Google/Gender.yml b/docs/_vale/Google/Gender.yml
similarity index 100%
rename from doc/_vale/Google/Gender.yml
rename to docs/_vale/Google/Gender.yml
diff --git a/doc/_vale/Google/GenderBias.yml b/docs/_vale/Google/GenderBias.yml
similarity index 100%
rename from doc/_vale/Google/GenderBias.yml
rename to docs/_vale/Google/GenderBias.yml
diff --git a/doc/_vale/Google/HeadingPunctuation.yml b/docs/_vale/Google/HeadingPunctuation.yml
similarity index 100%
rename from doc/_vale/Google/HeadingPunctuation.yml
rename to docs/_vale/Google/HeadingPunctuation.yml
diff --git a/doc/_vale/Google/Headings.yml b/docs/_vale/Google/Headings.yml
similarity index 100%
rename from doc/_vale/Google/Headings.yml
rename to docs/_vale/Google/Headings.yml
diff --git a/doc/_vale/Google/Latin.yml b/docs/_vale/Google/Latin.yml
similarity index 100%
rename from doc/_vale/Google/Latin.yml
rename to docs/_vale/Google/Latin.yml
diff --git a/doc/_vale/Google/LyHyphens.yml b/docs/_vale/Google/LyHyphens.yml
similarity index 100%
rename from doc/_vale/Google/LyHyphens.yml
rename to docs/_vale/Google/LyHyphens.yml
diff --git a/doc/_vale/Google/OptionalPlurals.yml b/docs/_vale/Google/OptionalPlurals.yml
similarity index 100%
rename from doc/_vale/Google/OptionalPlurals.yml
rename to docs/_vale/Google/OptionalPlurals.yml
diff --git a/doc/_vale/Google/Ordinal.yml b/docs/_vale/Google/Ordinal.yml
similarity index 100%
rename from doc/_vale/Google/Ordinal.yml
rename to docs/_vale/Google/Ordinal.yml
diff --git a/doc/_vale/Google/OxfordComma.yml b/docs/_vale/Google/OxfordComma.yml
similarity index 100%
rename from doc/_vale/Google/OxfordComma.yml
rename to docs/_vale/Google/OxfordComma.yml
diff --git a/doc/_vale/Google/Parens.yml b/docs/_vale/Google/Parens.yml
similarity index 100%
rename from doc/_vale/Google/Parens.yml
rename to docs/_vale/Google/Parens.yml
diff --git a/doc/_vale/Google/Passive.yml b/docs/_vale/Google/Passive.yml
similarity index 100%
rename from doc/_vale/Google/Passive.yml
rename to docs/_vale/Google/Passive.yml
diff --git a/doc/_vale/Google/Periods.yml b/docs/_vale/Google/Periods.yml
similarity index 100%
rename from doc/_vale/Google/Periods.yml
rename to docs/_vale/Google/Periods.yml
diff --git a/doc/_vale/Google/Quotes.yml b/docs/_vale/Google/Quotes.yml
similarity index 100%
rename from doc/_vale/Google/Quotes.yml
rename to docs/_vale/Google/Quotes.yml
diff --git a/doc/_vale/Google/Ranges.yml b/docs/_vale/Google/Ranges.yml
similarity index 100%
rename from doc/_vale/Google/Ranges.yml
rename to docs/_vale/Google/Ranges.yml
diff --git a/doc/_vale/Google/Semicolons.yml b/docs/_vale/Google/Semicolons.yml
similarity index 100%
rename from doc/_vale/Google/Semicolons.yml
rename to docs/_vale/Google/Semicolons.yml
diff --git a/doc/_vale/Google/Slang.yml b/docs/_vale/Google/Slang.yml
similarity index 100%
rename from doc/_vale/Google/Slang.yml
rename to docs/_vale/Google/Slang.yml
diff --git a/doc/_vale/Google/Spacing.yml b/docs/_vale/Google/Spacing.yml
similarity index 100%
rename from doc/_vale/Google/Spacing.yml
rename to docs/_vale/Google/Spacing.yml
diff --git a/doc/_vale/Google/Spelling.yml b/docs/_vale/Google/Spelling.yml
similarity index 100%
rename from doc/_vale/Google/Spelling.yml
rename to docs/_vale/Google/Spelling.yml
diff --git a/doc/_vale/Google/Units.yml b/docs/_vale/Google/Units.yml
similarity index 100%
rename from doc/_vale/Google/Units.yml
rename to docs/_vale/Google/Units.yml
diff --git a/doc/_vale/Google/We.yml b/docs/_vale/Google/We.yml
similarity index 100%
rename from doc/_vale/Google/We.yml
rename to docs/_vale/Google/We.yml
diff --git a/doc/_vale/Google/Will.yml b/docs/_vale/Google/Will.yml
similarity index 100%
rename from doc/_vale/Google/Will.yml
rename to docs/_vale/Google/Will.yml
diff --git a/doc/_vale/Google/WordList.yml b/docs/_vale/Google/WordList.yml
similarity index 100%
rename from doc/_vale/Google/WordList.yml
rename to docs/_vale/Google/WordList.yml
diff --git a/doc/_vale/Google/meta.json b/docs/_vale/Google/meta.json
similarity index 100%
rename from doc/_vale/Google/meta.json
rename to docs/_vale/Google/meta.json
diff --git a/docs/_vale/Google/vocab.txt b/docs/_vale/Google/vocab.txt
new file mode 100644
index 00000000..e69de29b
diff --git a/doc/_vale/Microsoft/AMPM.yml b/docs/_vale/Microsoft/AMPM.yml
similarity index 100%
rename from doc/_vale/Microsoft/AMPM.yml
rename to docs/_vale/Microsoft/AMPM.yml
diff --git a/doc/_vale/Microsoft/Accessibility.yml b/docs/_vale/Microsoft/Accessibility.yml
similarity index 100%
rename from doc/_vale/Microsoft/Accessibility.yml
rename to docs/_vale/Microsoft/Accessibility.yml
diff --git a/doc/_vale/Microsoft/Acronyms.yml b/docs/_vale/Microsoft/Acronyms.yml
similarity index 100%
rename from doc/_vale/Microsoft/Acronyms.yml
rename to docs/_vale/Microsoft/Acronyms.yml
diff --git a/doc/_vale/Microsoft/Adverbs.yml b/docs/_vale/Microsoft/Adverbs.yml
similarity index 100%
rename from doc/_vale/Microsoft/Adverbs.yml
rename to docs/_vale/Microsoft/Adverbs.yml
diff --git a/doc/_vale/Microsoft/Auto.yml b/docs/_vale/Microsoft/Auto.yml
similarity index 100%
rename from doc/_vale/Microsoft/Auto.yml
rename to docs/_vale/Microsoft/Auto.yml
diff --git a/doc/_vale/Microsoft/Avoid.yml b/docs/_vale/Microsoft/Avoid.yml
similarity index 100%
rename from doc/_vale/Microsoft/Avoid.yml
rename to docs/_vale/Microsoft/Avoid.yml
diff --git a/doc/_vale/Microsoft/ComplexWords.yml b/docs/_vale/Microsoft/ComplexWords.yml
similarity index 100%
rename from doc/_vale/Microsoft/ComplexWords.yml
rename to docs/_vale/Microsoft/ComplexWords.yml
diff --git a/doc/_vale/Microsoft/Contractions.yml b/docs/_vale/Microsoft/Contractions.yml
similarity index 100%
rename from doc/_vale/Microsoft/Contractions.yml
rename to docs/_vale/Microsoft/Contractions.yml
diff --git a/doc/_vale/Microsoft/Dashes.yml b/docs/_vale/Microsoft/Dashes.yml
similarity index 100%
rename from doc/_vale/Microsoft/Dashes.yml
rename to docs/_vale/Microsoft/Dashes.yml
diff --git a/doc/_vale/Microsoft/DateFormat.yml b/docs/_vale/Microsoft/DateFormat.yml
similarity index 100%
rename from doc/_vale/Microsoft/DateFormat.yml
rename to docs/_vale/Microsoft/DateFormat.yml
diff --git a/doc/_vale/Microsoft/DateNumbers.yml b/docs/_vale/Microsoft/DateNumbers.yml
similarity index 100%
rename from doc/_vale/Microsoft/DateNumbers.yml
rename to docs/_vale/Microsoft/DateNumbers.yml
diff --git a/doc/_vale/Microsoft/DateOrder.yml b/docs/_vale/Microsoft/DateOrder.yml
similarity index 100%
rename from doc/_vale/Microsoft/DateOrder.yml
rename to docs/_vale/Microsoft/DateOrder.yml
diff --git a/doc/_vale/Microsoft/Ellipses.yml b/docs/_vale/Microsoft/Ellipses.yml
similarity index 100%
rename from doc/_vale/Microsoft/Ellipses.yml
rename to docs/_vale/Microsoft/Ellipses.yml
diff --git a/doc/_vale/Microsoft/FirstPerson.yml b/docs/_vale/Microsoft/FirstPerson.yml
similarity index 100%
rename from doc/_vale/Microsoft/FirstPerson.yml
rename to docs/_vale/Microsoft/FirstPerson.yml
diff --git a/doc/_vale/Microsoft/Foreign.yml b/docs/_vale/Microsoft/Foreign.yml
similarity index 100%
rename from doc/_vale/Microsoft/Foreign.yml
rename to docs/_vale/Microsoft/Foreign.yml
diff --git a/doc/_vale/Microsoft/Gender.yml b/docs/_vale/Microsoft/Gender.yml
similarity index 100%
rename from doc/_vale/Microsoft/Gender.yml
rename to docs/_vale/Microsoft/Gender.yml
diff --git a/doc/_vale/Microsoft/GenderBias.yml b/docs/_vale/Microsoft/GenderBias.yml
similarity index 100%
rename from doc/_vale/Microsoft/GenderBias.yml
rename to docs/_vale/Microsoft/GenderBias.yml
diff --git a/doc/_vale/Microsoft/GeneralURL.yml b/docs/_vale/Microsoft/GeneralURL.yml
similarity index 100%
rename from doc/_vale/Microsoft/GeneralURL.yml
rename to docs/_vale/Microsoft/GeneralURL.yml
diff --git a/doc/_vale/Microsoft/HeadingAcronyms.yml b/docs/_vale/Microsoft/HeadingAcronyms.yml
similarity index 100%
rename from doc/_vale/Microsoft/HeadingAcronyms.yml
rename to docs/_vale/Microsoft/HeadingAcronyms.yml
diff --git a/doc/_vale/Microsoft/HeadingColons.yml b/docs/_vale/Microsoft/HeadingColons.yml
similarity index 100%
rename from doc/_vale/Microsoft/HeadingColons.yml
rename to docs/_vale/Microsoft/HeadingColons.yml
diff --git a/doc/_vale/Microsoft/HeadingPunctuation.yml b/docs/_vale/Microsoft/HeadingPunctuation.yml
similarity index 100%
rename from doc/_vale/Microsoft/HeadingPunctuation.yml
rename to docs/_vale/Microsoft/HeadingPunctuation.yml
diff --git a/doc/_vale/Microsoft/Headings.yml b/docs/_vale/Microsoft/Headings.yml
similarity index 100%
rename from doc/_vale/Microsoft/Headings.yml
rename to docs/_vale/Microsoft/Headings.yml
diff --git a/doc/_vale/Microsoft/Hyphens.yml b/docs/_vale/Microsoft/Hyphens.yml
similarity index 100%
rename from doc/_vale/Microsoft/Hyphens.yml
rename to docs/_vale/Microsoft/Hyphens.yml
diff --git a/doc/_vale/Microsoft/Negative.yml b/docs/_vale/Microsoft/Negative.yml
similarity index 100%
rename from doc/_vale/Microsoft/Negative.yml
rename to docs/_vale/Microsoft/Negative.yml
diff --git a/doc/_vale/Microsoft/Ordinal.yml b/docs/_vale/Microsoft/Ordinal.yml
similarity index 100%
rename from doc/_vale/Microsoft/Ordinal.yml
rename to docs/_vale/Microsoft/Ordinal.yml
diff --git a/doc/_vale/Microsoft/OxfordComma.yml b/docs/_vale/Microsoft/OxfordComma.yml
similarity index 100%
rename from doc/_vale/Microsoft/OxfordComma.yml
rename to docs/_vale/Microsoft/OxfordComma.yml
diff --git a/doc/_vale/Microsoft/Passive.yml b/docs/_vale/Microsoft/Passive.yml
similarity index 100%
rename from doc/_vale/Microsoft/Passive.yml
rename to docs/_vale/Microsoft/Passive.yml
diff --git a/doc/_vale/Microsoft/Percentages.yml b/docs/_vale/Microsoft/Percentages.yml
similarity index 100%
rename from doc/_vale/Microsoft/Percentages.yml
rename to docs/_vale/Microsoft/Percentages.yml
diff --git a/doc/_vale/Microsoft/Quotes.yml b/docs/_vale/Microsoft/Quotes.yml
similarity index 100%
rename from doc/_vale/Microsoft/Quotes.yml
rename to docs/_vale/Microsoft/Quotes.yml
diff --git a/doc/_vale/Microsoft/RangeFormat.yml b/docs/_vale/Microsoft/RangeFormat.yml
similarity index 100%
rename from doc/_vale/Microsoft/RangeFormat.yml
rename to docs/_vale/Microsoft/RangeFormat.yml
diff --git a/doc/_vale/Microsoft/RangeTime.yml b/docs/_vale/Microsoft/RangeTime.yml
similarity index 100%
rename from doc/_vale/Microsoft/RangeTime.yml
rename to docs/_vale/Microsoft/RangeTime.yml
diff --git a/doc/_vale/Microsoft/Ranges.yml b/docs/_vale/Microsoft/Ranges.yml
similarity index 100%
rename from doc/_vale/Microsoft/Ranges.yml
rename to docs/_vale/Microsoft/Ranges.yml
diff --git a/doc/_vale/Microsoft/Semicolon.yml b/docs/_vale/Microsoft/Semicolon.yml
similarity index 100%
rename from doc/_vale/Microsoft/Semicolon.yml
rename to docs/_vale/Microsoft/Semicolon.yml
diff --git a/doc/_vale/Microsoft/SentenceLength.yml b/docs/_vale/Microsoft/SentenceLength.yml
similarity index 100%
rename from doc/_vale/Microsoft/SentenceLength.yml
rename to docs/_vale/Microsoft/SentenceLength.yml
diff --git a/doc/_vale/Microsoft/Spacing.yml b/docs/_vale/Microsoft/Spacing.yml
similarity index 100%
rename from doc/_vale/Microsoft/Spacing.yml
rename to docs/_vale/Microsoft/Spacing.yml
diff --git a/doc/_vale/Microsoft/Suspended.yml b/docs/_vale/Microsoft/Suspended.yml
similarity index 100%
rename from doc/_vale/Microsoft/Suspended.yml
rename to docs/_vale/Microsoft/Suspended.yml
diff --git a/doc/_vale/Microsoft/Terms.yml b/docs/_vale/Microsoft/Terms.yml
similarity index 100%
rename from doc/_vale/Microsoft/Terms.yml
rename to docs/_vale/Microsoft/Terms.yml
diff --git a/doc/_vale/Microsoft/URLFormat.yml b/docs/_vale/Microsoft/URLFormat.yml
similarity index 100%
rename from doc/_vale/Microsoft/URLFormat.yml
rename to docs/_vale/Microsoft/URLFormat.yml
diff --git a/doc/_vale/Microsoft/Units.yml b/docs/_vale/Microsoft/Units.yml
similarity index 100%
rename from doc/_vale/Microsoft/Units.yml
rename to docs/_vale/Microsoft/Units.yml
diff --git a/doc/_vale/Microsoft/Vocab.yml b/docs/_vale/Microsoft/Vocab.yml
similarity index 100%
rename from doc/_vale/Microsoft/Vocab.yml
rename to docs/_vale/Microsoft/Vocab.yml
diff --git a/doc/_vale/Microsoft/We.yml b/docs/_vale/Microsoft/We.yml
similarity index 100%
rename from doc/_vale/Microsoft/We.yml
rename to docs/_vale/Microsoft/We.yml
diff --git a/doc/_vale/Microsoft/Wordiness.yml b/docs/_vale/Microsoft/Wordiness.yml
similarity index 100%
rename from doc/_vale/Microsoft/Wordiness.yml
rename to docs/_vale/Microsoft/Wordiness.yml
diff --git a/doc/_vale/Microsoft/meta.json b/docs/_vale/Microsoft/meta.json
similarity index 100%
rename from doc/_vale/Microsoft/meta.json
rename to docs/_vale/Microsoft/meta.json
diff --git a/doc/_vale/RedHat/Abbreviations.yml b/docs/_vale/RedHat/Abbreviations.yml
similarity index 100%
rename from doc/_vale/RedHat/Abbreviations.yml
rename to docs/_vale/RedHat/Abbreviations.yml
diff --git a/doc/_vale/RedHat/CaseSensitiveTerms.yml b/docs/_vale/RedHat/CaseSensitiveTerms.yml
similarity index 100%
rename from doc/_vale/RedHat/CaseSensitiveTerms.yml
rename to docs/_vale/RedHat/CaseSensitiveTerms.yml
diff --git a/doc/_vale/RedHat/Conjunctions.yml b/docs/_vale/RedHat/Conjunctions.yml
similarity index 100%
rename from doc/_vale/RedHat/Conjunctions.yml
rename to docs/_vale/RedHat/Conjunctions.yml
diff --git a/doc/_vale/RedHat/ConsciousLanguage.yml b/docs/_vale/RedHat/ConsciousLanguage.yml
similarity index 100%
rename from doc/_vale/RedHat/ConsciousLanguage.yml
rename to docs/_vale/RedHat/ConsciousLanguage.yml
diff --git a/doc/_vale/RedHat/Contractions.yml b/docs/_vale/RedHat/Contractions.yml
similarity index 100%
rename from doc/_vale/RedHat/Contractions.yml
rename to docs/_vale/RedHat/Contractions.yml
diff --git a/doc/_vale/RedHat/Definitions.yml b/docs/_vale/RedHat/Definitions.yml
similarity index 100%
rename from doc/_vale/RedHat/Definitions.yml
rename to docs/_vale/RedHat/Definitions.yml
diff --git a/doc/_vale/RedHat/DoNotUseTerms.yml b/docs/_vale/RedHat/DoNotUseTerms.yml
similarity index 100%
rename from doc/_vale/RedHat/DoNotUseTerms.yml
rename to docs/_vale/RedHat/DoNotUseTerms.yml
diff --git a/doc/_vale/RedHat/Ellipses.yml b/docs/_vale/RedHat/Ellipses.yml
similarity index 100%
rename from doc/_vale/RedHat/Ellipses.yml
rename to docs/_vale/RedHat/Ellipses.yml
diff --git a/doc/_vale/RedHat/HeadingPunctuation.yml b/docs/_vale/RedHat/HeadingPunctuation.yml
similarity index 100%
rename from doc/_vale/RedHat/HeadingPunctuation.yml
rename to docs/_vale/RedHat/HeadingPunctuation.yml
diff --git a/doc/_vale/RedHat/Headings.yml b/docs/_vale/RedHat/Headings.yml
similarity index 100%
rename from doc/_vale/RedHat/Headings.yml
rename to docs/_vale/RedHat/Headings.yml
diff --git a/doc/_vale/RedHat/Hyphens.yml b/docs/_vale/RedHat/Hyphens.yml
similarity index 100%
rename from doc/_vale/RedHat/Hyphens.yml
rename to docs/_vale/RedHat/Hyphens.yml
diff --git a/doc/_vale/RedHat/OxfordComma.yml b/docs/_vale/RedHat/OxfordComma.yml
similarity index 100%
rename from doc/_vale/RedHat/OxfordComma.yml
rename to docs/_vale/RedHat/OxfordComma.yml
diff --git a/doc/_vale/RedHat/PascalCamelCase.yml b/docs/_vale/RedHat/PascalCamelCase.yml
similarity index 100%
rename from doc/_vale/RedHat/PascalCamelCase.yml
rename to docs/_vale/RedHat/PascalCamelCase.yml
diff --git a/doc/_vale/RedHat/PassiveVoice.yml b/docs/_vale/RedHat/PassiveVoice.yml
similarity index 100%
rename from doc/_vale/RedHat/PassiveVoice.yml
rename to docs/_vale/RedHat/PassiveVoice.yml
diff --git a/doc/_vale/RedHat/README-IBM.adoc b/docs/_vale/RedHat/README-IBM.adoc
similarity index 100%
rename from doc/_vale/RedHat/README-IBM.adoc
rename to docs/_vale/RedHat/README-IBM.adoc
diff --git a/doc/_vale/RedHat/README-proselint.md b/docs/_vale/RedHat/README-proselint.md
similarity index 100%
rename from doc/_vale/RedHat/README-proselint.md
rename to docs/_vale/RedHat/README-proselint.md
diff --git a/doc/_vale/RedHat/README-write-good.md b/docs/_vale/RedHat/README-write-good.md
similarity index 100%
rename from doc/_vale/RedHat/README-write-good.md
rename to docs/_vale/RedHat/README-write-good.md
diff --git a/doc/_vale/RedHat/ReadabilityGrade.yml b/docs/_vale/RedHat/ReadabilityGrade.yml
similarity index 100%
rename from doc/_vale/RedHat/ReadabilityGrade.yml
rename to docs/_vale/RedHat/ReadabilityGrade.yml
diff --git a/doc/_vale/RedHat/ReleaseNotes.yml b/docs/_vale/RedHat/ReleaseNotes.yml
similarity index 100%
rename from doc/_vale/RedHat/ReleaseNotes.yml
rename to docs/_vale/RedHat/ReleaseNotes.yml
diff --git a/doc/_vale/RedHat/RepeatedWords.yml b/docs/_vale/RedHat/RepeatedWords.yml
similarity index 100%
rename from doc/_vale/RedHat/RepeatedWords.yml
rename to docs/_vale/RedHat/RepeatedWords.yml
diff --git a/doc/_vale/RedHat/SelfReferentialText.yml b/docs/_vale/RedHat/SelfReferentialText.yml
similarity index 100%
rename from doc/_vale/RedHat/SelfReferentialText.yml
rename to docs/_vale/RedHat/SelfReferentialText.yml
diff --git a/doc/_vale/RedHat/SentenceLength.yml b/docs/_vale/RedHat/SentenceLength.yml
similarity index 100%
rename from doc/_vale/RedHat/SentenceLength.yml
rename to docs/_vale/RedHat/SentenceLength.yml
diff --git a/doc/_vale/RedHat/SimpleWords.yml b/docs/_vale/RedHat/SimpleWords.yml
similarity index 100%
rename from doc/_vale/RedHat/SimpleWords.yml
rename to docs/_vale/RedHat/SimpleWords.yml
diff --git a/doc/_vale/RedHat/Slash.yml b/docs/_vale/RedHat/Slash.yml
similarity index 100%
rename from doc/_vale/RedHat/Slash.yml
rename to docs/_vale/RedHat/Slash.yml
diff --git a/doc/_vale/RedHat/Spacing.yml b/docs/_vale/RedHat/Spacing.yml
similarity index 100%
rename from doc/_vale/RedHat/Spacing.yml
rename to docs/_vale/RedHat/Spacing.yml
diff --git a/doc/_vale/RedHat/Spelling.yml b/docs/_vale/RedHat/Spelling.yml
similarity index 100%
rename from doc/_vale/RedHat/Spelling.yml
rename to docs/_vale/RedHat/Spelling.yml
diff --git a/doc/_vale/RedHat/Symbols.yml b/docs/_vale/RedHat/Symbols.yml
similarity index 100%
rename from doc/_vale/RedHat/Symbols.yml
rename to docs/_vale/RedHat/Symbols.yml
diff --git a/doc/_vale/RedHat/TermsErrors.yml b/docs/_vale/RedHat/TermsErrors.yml
similarity index 100%
rename from doc/_vale/RedHat/TermsErrors.yml
rename to docs/_vale/RedHat/TermsErrors.yml
diff --git a/doc/_vale/RedHat/TermsSuggestions.yml b/docs/_vale/RedHat/TermsSuggestions.yml
similarity index 100%
rename from doc/_vale/RedHat/TermsSuggestions.yml
rename to docs/_vale/RedHat/TermsSuggestions.yml
diff --git a/doc/_vale/RedHat/TermsWarnings.yml b/docs/_vale/RedHat/TermsWarnings.yml
similarity index 100%
rename from doc/_vale/RedHat/TermsWarnings.yml
rename to docs/_vale/RedHat/TermsWarnings.yml
diff --git a/doc/_vale/RedHat/UserReplacedValues.yml b/docs/_vale/RedHat/UserReplacedValues.yml
similarity index 100%
rename from doc/_vale/RedHat/UserReplacedValues.yml
rename to docs/_vale/RedHat/UserReplacedValues.yml
diff --git a/doc/_vale/RedHat/collate-output.tmpl b/docs/_vale/RedHat/collate-output.tmpl
similarity index 100%
rename from doc/_vale/RedHat/collate-output.tmpl
rename to docs/_vale/RedHat/collate-output.tmpl
diff --git a/doc/_vale/RedHat/meta.json b/docs/_vale/RedHat/meta.json
similarity index 100%
rename from doc/_vale/RedHat/meta.json
rename to docs/_vale/RedHat/meta.json
diff --git a/doc/_vale/alex/Ablist.yml b/docs/_vale/alex/Ablist.yml
similarity index 100%
rename from doc/_vale/alex/Ablist.yml
rename to docs/_vale/alex/Ablist.yml
diff --git a/doc/_vale/alex/Condescending.yml b/docs/_vale/alex/Condescending.yml
similarity index 100%
rename from doc/_vale/alex/Condescending.yml
rename to docs/_vale/alex/Condescending.yml
diff --git a/doc/_vale/alex/Gendered.yml b/docs/_vale/alex/Gendered.yml
similarity index 100%
rename from doc/_vale/alex/Gendered.yml
rename to docs/_vale/alex/Gendered.yml
diff --git a/doc/_vale/alex/LGBTQ.yml b/docs/_vale/alex/LGBTQ.yml
similarity index 100%
rename from doc/_vale/alex/LGBTQ.yml
rename to docs/_vale/alex/LGBTQ.yml
diff --git a/doc/_vale/alex/Press.yml b/docs/_vale/alex/Press.yml
similarity index 100%
rename from doc/_vale/alex/Press.yml
rename to docs/_vale/alex/Press.yml
diff --git a/doc/_vale/alex/ProfanityLikely.yml b/docs/_vale/alex/ProfanityLikely.yml
similarity index 100%
rename from doc/_vale/alex/ProfanityLikely.yml
rename to docs/_vale/alex/ProfanityLikely.yml
diff --git a/doc/_vale/alex/ProfanityMaybe.yml b/docs/_vale/alex/ProfanityMaybe.yml
similarity index 100%
rename from doc/_vale/alex/ProfanityMaybe.yml
rename to docs/_vale/alex/ProfanityMaybe.yml
diff --git a/doc/_vale/alex/ProfanityUnlikely.yml b/docs/_vale/alex/ProfanityUnlikely.yml
similarity index 100%
rename from doc/_vale/alex/ProfanityUnlikely.yml
rename to docs/_vale/alex/ProfanityUnlikely.yml
diff --git a/doc/_vale/alex/README.md b/docs/_vale/alex/README.md
similarity index 100%
rename from doc/_vale/alex/README.md
rename to docs/_vale/alex/README.md
diff --git a/doc/_vale/alex/Race.yml b/docs/_vale/alex/Race.yml
similarity index 100%
rename from doc/_vale/alex/Race.yml
rename to docs/_vale/alex/Race.yml
diff --git a/doc/_vale/alex/Suicide.yml b/docs/_vale/alex/Suicide.yml
similarity index 100%
rename from doc/_vale/alex/Suicide.yml
rename to docs/_vale/alex/Suicide.yml
diff --git a/doc/_vale/alex/meta.json b/docs/_vale/alex/meta.json
similarity index 100%
rename from doc/_vale/alex/meta.json
rename to docs/_vale/alex/meta.json
diff --git a/doc/_vale/config/vocabularies/EPNix/accept.txt b/docs/_vale/config/vocabularies/EPNix/accept.txt
similarity index 100%
rename from doc/_vale/config/vocabularies/EPNix/accept.txt
rename to docs/_vale/config/vocabularies/EPNix/accept.txt
diff --git a/doc/_vale/config/vocabularies/EPNix/reject.txt b/docs/_vale/config/vocabularies/EPNix/reject.txt
similarity index 100%
rename from doc/_vale/config/vocabularies/EPNix/reject.txt
rename to docs/_vale/config/vocabularies/EPNix/reject.txt
diff --git a/doc/_vale/proselint/Airlinese.yml b/docs/_vale/proselint/Airlinese.yml
similarity index 100%
rename from doc/_vale/proselint/Airlinese.yml
rename to docs/_vale/proselint/Airlinese.yml
diff --git a/doc/_vale/proselint/AnimalLabels.yml b/docs/_vale/proselint/AnimalLabels.yml
similarity index 100%
rename from doc/_vale/proselint/AnimalLabels.yml
rename to docs/_vale/proselint/AnimalLabels.yml
diff --git a/doc/_vale/proselint/Annotations.yml b/docs/_vale/proselint/Annotations.yml
similarity index 100%
rename from doc/_vale/proselint/Annotations.yml
rename to docs/_vale/proselint/Annotations.yml
diff --git a/doc/_vale/proselint/Apologizing.yml b/docs/_vale/proselint/Apologizing.yml
similarity index 100%
rename from doc/_vale/proselint/Apologizing.yml
rename to docs/_vale/proselint/Apologizing.yml
diff --git a/doc/_vale/proselint/Archaisms.yml b/docs/_vale/proselint/Archaisms.yml
similarity index 100%
rename from doc/_vale/proselint/Archaisms.yml
rename to docs/_vale/proselint/Archaisms.yml
diff --git a/doc/_vale/proselint/But.yml b/docs/_vale/proselint/But.yml
similarity index 100%
rename from doc/_vale/proselint/But.yml
rename to docs/_vale/proselint/But.yml
diff --git a/doc/_vale/proselint/Cliches.yml b/docs/_vale/proselint/Cliches.yml
similarity index 100%
rename from doc/_vale/proselint/Cliches.yml
rename to docs/_vale/proselint/Cliches.yml
diff --git a/doc/_vale/proselint/CorporateSpeak.yml b/docs/_vale/proselint/CorporateSpeak.yml
similarity index 100%
rename from doc/_vale/proselint/CorporateSpeak.yml
rename to docs/_vale/proselint/CorporateSpeak.yml
diff --git a/doc/_vale/proselint/Currency.yml b/docs/_vale/proselint/Currency.yml
similarity index 100%
rename from doc/_vale/proselint/Currency.yml
rename to docs/_vale/proselint/Currency.yml
diff --git a/doc/_vale/proselint/Cursing.yml b/docs/_vale/proselint/Cursing.yml
similarity index 100%
rename from doc/_vale/proselint/Cursing.yml
rename to docs/_vale/proselint/Cursing.yml
diff --git a/doc/_vale/proselint/DateCase.yml b/docs/_vale/proselint/DateCase.yml
similarity index 100%
rename from doc/_vale/proselint/DateCase.yml
rename to docs/_vale/proselint/DateCase.yml
diff --git a/doc/_vale/proselint/DateMidnight.yml b/docs/_vale/proselint/DateMidnight.yml
similarity index 100%
rename from doc/_vale/proselint/DateMidnight.yml
rename to docs/_vale/proselint/DateMidnight.yml
diff --git a/doc/_vale/proselint/DateRedundancy.yml b/docs/_vale/proselint/DateRedundancy.yml
similarity index 100%
rename from doc/_vale/proselint/DateRedundancy.yml
rename to docs/_vale/proselint/DateRedundancy.yml
diff --git a/doc/_vale/proselint/DateSpacing.yml b/docs/_vale/proselint/DateSpacing.yml
similarity index 100%
rename from doc/_vale/proselint/DateSpacing.yml
rename to docs/_vale/proselint/DateSpacing.yml
diff --git a/doc/_vale/proselint/DenizenLabels.yml b/docs/_vale/proselint/DenizenLabels.yml
similarity index 100%
rename from doc/_vale/proselint/DenizenLabels.yml
rename to docs/_vale/proselint/DenizenLabels.yml
diff --git a/doc/_vale/proselint/Diacritical.yml b/docs/_vale/proselint/Diacritical.yml
similarity index 100%
rename from doc/_vale/proselint/Diacritical.yml
rename to docs/_vale/proselint/Diacritical.yml
diff --git a/doc/_vale/proselint/GenderBias.yml b/docs/_vale/proselint/GenderBias.yml
similarity index 100%
rename from doc/_vale/proselint/GenderBias.yml
rename to docs/_vale/proselint/GenderBias.yml
diff --git a/doc/_vale/proselint/GroupTerms.yml b/docs/_vale/proselint/GroupTerms.yml
similarity index 100%
rename from doc/_vale/proselint/GroupTerms.yml
rename to docs/_vale/proselint/GroupTerms.yml
diff --git a/doc/_vale/proselint/Hedging.yml b/docs/_vale/proselint/Hedging.yml
similarity index 100%
rename from doc/_vale/proselint/Hedging.yml
rename to docs/_vale/proselint/Hedging.yml
diff --git a/doc/_vale/proselint/Hyperbole.yml b/docs/_vale/proselint/Hyperbole.yml
similarity index 100%
rename from doc/_vale/proselint/Hyperbole.yml
rename to docs/_vale/proselint/Hyperbole.yml
diff --git a/doc/_vale/proselint/Jargon.yml b/docs/_vale/proselint/Jargon.yml
similarity index 100%
rename from doc/_vale/proselint/Jargon.yml
rename to docs/_vale/proselint/Jargon.yml
diff --git a/doc/_vale/proselint/LGBTOffensive.yml b/docs/_vale/proselint/LGBTOffensive.yml
similarity index 100%
rename from doc/_vale/proselint/LGBTOffensive.yml
rename to docs/_vale/proselint/LGBTOffensive.yml
diff --git a/doc/_vale/proselint/LGBTTerms.yml b/docs/_vale/proselint/LGBTTerms.yml
similarity index 100%
rename from doc/_vale/proselint/LGBTTerms.yml
rename to docs/_vale/proselint/LGBTTerms.yml
diff --git a/doc/_vale/proselint/Malapropisms.yml b/docs/_vale/proselint/Malapropisms.yml
similarity index 100%
rename from doc/_vale/proselint/Malapropisms.yml
rename to docs/_vale/proselint/Malapropisms.yml
diff --git a/doc/_vale/proselint/Needless.yml b/docs/_vale/proselint/Needless.yml
similarity index 100%
rename from doc/_vale/proselint/Needless.yml
rename to docs/_vale/proselint/Needless.yml
diff --git a/doc/_vale/proselint/Nonwords.yml b/docs/_vale/proselint/Nonwords.yml
similarity index 100%
rename from doc/_vale/proselint/Nonwords.yml
rename to docs/_vale/proselint/Nonwords.yml
diff --git a/doc/_vale/proselint/Oxymorons.yml b/docs/_vale/proselint/Oxymorons.yml
similarity index 100%
rename from doc/_vale/proselint/Oxymorons.yml
rename to docs/_vale/proselint/Oxymorons.yml
diff --git a/doc/_vale/proselint/P-Value.yml b/docs/_vale/proselint/P-Value.yml
similarity index 100%
rename from doc/_vale/proselint/P-Value.yml
rename to docs/_vale/proselint/P-Value.yml
diff --git a/doc/_vale/proselint/RASSyndrome.yml b/docs/_vale/proselint/RASSyndrome.yml
similarity index 100%
rename from doc/_vale/proselint/RASSyndrome.yml
rename to docs/_vale/proselint/RASSyndrome.yml
diff --git a/doc/_vale/proselint/README.md b/docs/_vale/proselint/README.md
similarity index 100%
rename from doc/_vale/proselint/README.md
rename to docs/_vale/proselint/README.md
diff --git a/doc/_vale/proselint/Skunked.yml b/docs/_vale/proselint/Skunked.yml
similarity index 100%
rename from doc/_vale/proselint/Skunked.yml
rename to docs/_vale/proselint/Skunked.yml
diff --git a/doc/_vale/proselint/Spelling.yml b/docs/_vale/proselint/Spelling.yml
similarity index 100%
rename from doc/_vale/proselint/Spelling.yml
rename to docs/_vale/proselint/Spelling.yml
diff --git a/doc/_vale/proselint/Typography.yml b/docs/_vale/proselint/Typography.yml
similarity index 100%
rename from doc/_vale/proselint/Typography.yml
rename to docs/_vale/proselint/Typography.yml
diff --git a/doc/_vale/proselint/Uncomparables.yml b/docs/_vale/proselint/Uncomparables.yml
similarity index 100%
rename from doc/_vale/proselint/Uncomparables.yml
rename to docs/_vale/proselint/Uncomparables.yml
diff --git a/doc/_vale/proselint/Very.yml b/docs/_vale/proselint/Very.yml
similarity index 100%
rename from doc/_vale/proselint/Very.yml
rename to docs/_vale/proselint/Very.yml
diff --git a/doc/_vale/proselint/meta.json b/docs/_vale/proselint/meta.json
similarity index 100%
rename from doc/_vale/proselint/meta.json
rename to docs/_vale/proselint/meta.json
diff --git a/doc/_vale/write-good/Cliches.yml b/docs/_vale/write-good/Cliches.yml
similarity index 100%
rename from doc/_vale/write-good/Cliches.yml
rename to docs/_vale/write-good/Cliches.yml
diff --git a/doc/_vale/write-good/E-Prime.yml b/docs/_vale/write-good/E-Prime.yml
similarity index 100%
rename from doc/_vale/write-good/E-Prime.yml
rename to docs/_vale/write-good/E-Prime.yml
diff --git a/doc/_vale/write-good/Illusions.yml b/docs/_vale/write-good/Illusions.yml
similarity index 100%
rename from doc/_vale/write-good/Illusions.yml
rename to docs/_vale/write-good/Illusions.yml
diff --git a/doc/_vale/write-good/Passive.yml b/docs/_vale/write-good/Passive.yml
similarity index 100%
rename from doc/_vale/write-good/Passive.yml
rename to docs/_vale/write-good/Passive.yml
diff --git a/doc/_vale/write-good/README.md b/docs/_vale/write-good/README.md
similarity index 100%
rename from doc/_vale/write-good/README.md
rename to docs/_vale/write-good/README.md
diff --git a/doc/_vale/write-good/So.yml b/docs/_vale/write-good/So.yml
similarity index 100%
rename from doc/_vale/write-good/So.yml
rename to docs/_vale/write-good/So.yml
diff --git a/doc/_vale/write-good/ThereIs.yml b/docs/_vale/write-good/ThereIs.yml
similarity index 100%
rename from doc/_vale/write-good/ThereIs.yml
rename to docs/_vale/write-good/ThereIs.yml
diff --git a/doc/_vale/write-good/TooWordy.yml b/docs/_vale/write-good/TooWordy.yml
similarity index 100%
rename from doc/_vale/write-good/TooWordy.yml
rename to docs/_vale/write-good/TooWordy.yml
diff --git a/doc/_vale/write-good/Weasel.yml b/docs/_vale/write-good/Weasel.yml
similarity index 100%
rename from doc/_vale/write-good/Weasel.yml
rename to docs/_vale/write-good/Weasel.yml
diff --git a/doc/_vale/write-good/meta.json b/docs/_vale/write-good/meta.json
similarity index 100%
rename from doc/_vale/write-good/meta.json
rename to docs/_vale/write-good/meta.json
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 00000000..a56cfb77
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,112 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# For the full list of built-in configuration values, see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+import os
+import sys
+
+# Enables importing our custom "pygments_styles" module
+sys.path.append(os.path.abspath("./_ext"))
+
+# -- Project information -----------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
+
+project = "EPNix"
+copyright = "The EPNix Contributors"
+author = "The EPNix Contributors"
+
+language = "en"
+
+nitpicky = True
+
+# -- General configuration ---------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
+
+extensions = [
+ "myst_parser",
+ "sphinx.ext.githubpages",
+ "sphinx_copybutton",
+]
+
+templates_path = ["_templates"]
+exclude_patterns = [
+ "_build",
+ "Thumbs.db",
+ ".DS_Store",
+ "_vale",
+]
+
+# numfig = True
+
+pygments_style = "pygments_styles.EpnixNordLight"
+pygments_dark_style = "pygments_styles.EpnixNordDarker"
+
+# -- Options for MyST --------------------------------------------------------
+# https://myst-parser.readthedocs.io/en/latest/configuration.html
+
+myst_enable_extensions = [
+ "attrs_inline",
+ "colon_fence",
+ "deflist",
+]
+
+myst_url_schemes = {
+ "http": None,
+ "https": None,
+ "mailto": None,
+ "source": "https://github.com/epics-extensions/EPNix/blob/{{netloc}}{{path}}",
+ "gh-issue": {
+ "url": "https://github.com/executablebooks/MyST-Parser/issue/{{path}}#{{fragment}}",
+ "title": "Issue #{{path}}",
+ "classes": ["github"],
+ },
+}
+
+# -- Options for HTML output -------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
+
+html_static_path = ["_static"]
+html_baseurl = "https://epics-extensions.github.io/EPNix/"
+
+html_theme = "furo"
+html_theme_options = {
+ "source_repository": "https://github.com/epics-extensions/EPNix",
+ "source_branch": "master",
+ "source_directory": "docs/",
+ "dark_css_variables": {
+ "color-brand-primary": "#7ebae4",
+ "color-brand-content": "#7ebae4",
+ },
+ "light_css_variables": {
+ "color-brand-primary": "#415e9a",
+ "color-brand-content": "#415e9a",
+ },
+ "footer_icons": [
+ {
+ "name": "GitHub",
+ "url": "https://github.com/epics-extensions/EPNix",
+ "html": """
+
+ """,
+ "class": "",
+ },
+ ],
+}
+
+html_logo = "logo.svg"
+html_favicon = "favicon.svg"
+
+# -- Options for Man output --------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-manual-page-output
+
+man_pages = [
+ ("ioc/references/options", "epnix-ioc", "IOC options reference", "", 5),
+ ("ioc/references/packages", "epnix-ioc-packages", "", "", 5),
+ ("nixos-services/options", "epnix-nixos", "", "", 5),
+ ("pkgs/packages", "epnix-packages", "", "", 5),
+]
+
+man_show_urls = True
diff --git a/docs/favicon.svg b/docs/favicon.svg
new file mode 100644
index 00000000..3a4bca6b
--- /dev/null
+++ b/docs/favicon.svg
@@ -0,0 +1,277 @@
+
+
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 00000000..dd41b3a3
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,111 @@
+EPNix documentation
+===================
+
+.. figure:: logo.svg
+ :alt: EPNix logo
+
+ EPNix logo
+
+Introduction
+------------
+
+EPNix (pronunciation: like you are high on mushrooms) packages EPICS-related software using the `Nix`_ package manager.
+
+It’s made of three parts:
+
+- the EPICS IOC framework
+- other EPICS-related packages
+- NixOS modules
+
+The EPICS IOC framework lets you package, deploy, and test EPICS IOCs
+using the Nix package manager, which provides several benefits.
+For more information, see the :doc:`EPICS IOCs introduction `.
+
+EPNix also packages other EPICS-related tools, like procServ, Phoebus, etc.
+You can build them using Nix, and in the future download them pre-compiled, while having a strong guarantee that they will work as-is.
+For a list of all supported EPICS-related packages, see the :doc:`pkgs/packages`.
+
+EPNix also provides NixOS modules, which are instructions on how to configure various EPICS-related services on NixOS machines (for example the Phoebus alarm server).
+EPNix strives to have integration tests for each of those modules.
+For more information, see the :doc:`NixOS services introduction `.
+
+.. _Nix: https://nixos.org/guides/how-nix-works/
+
+Packaging policy
+~~~~~~~~~~~~~~~~
+
+As EPNix provides a package repository, packaging for example ``epics-base``, ``asyn``, ``StreamDevice``, ``procServ``, ``phoebus``, etc., it needs to have a packaging policy.
+
+In its package repository, EPNix officially supports the latest upstream version.
+
+However, since EPNix is a git repository, you will be able, through Nix, to use a fixed version of EPNix, without being forced to upgrade your dependencies.
+
+.. TODO: link to an explanation, from the IOC side, and from the NixOS side
+
+The epics-base package
+^^^^^^^^^^^^^^^^^^^^^^
+
+The epics-base package has no significant modification compared to the upstream version at `Launchpad`_.
+One goal of EPNix is to keep those modifications to a minimum, and upstream what’s possible.
+
+.. _Launchpad: https://git.launchpad.net/epics-base
+
+Release branches
+~~~~~~~~~~~~~~~~
+
+EPNix has a ``master`` branch,
+which is considered unstable,
+meaning breaking changes might happen without notice.
+
+EPNix also has release branches,
+such as ``nixos-23.11``,
+tied to the nixpkgs release branches,
+where breaking changes are forbidden.
+
+Backporting changes to older release branches is done on a “best-effort” basis.
+
+--------------
+
+This documentation follows the `Diátaxis`_ documentation framework.
+
+.. _Diátaxis: https://diataxis.fr/
+
+.. toctree::
+ :caption: EPICS IOCs
+ :hidden:
+ :titlesonly:
+
+ ioc/index
+ ioc/tutorials/index
+ ioc/user-guides/index
+ ioc/references/options
+ ioc/references/packages
+ ioc/faq
+
+.. toctree::
+ :caption: Packages
+ :hidden:
+ :titlesonly:
+
+ pkgs/packages
+
+.. toctree::
+ :caption: NixOS services
+ :hidden:
+ :titlesonly:
+
+ nixos-services/index
+ nixos-services/tutorials/index
+ nixos-services/user-guides/index
+ nixos-services/options
+
+.. toctree::
+ :caption: Release notes
+ :hidden:
+ :titlesonly:
+ :glob:
+ :reversed:
+
+ release-notes/*
+
+.. TODO: link an index to Nix options and packages
diff --git a/docs/ioc/faq.rst b/docs/ioc/faq.rst
new file mode 100644
index 00000000..b5ac60a0
--- /dev/null
+++ b/docs/ioc/faq.rst
@@ -0,0 +1,50 @@
+Frequently Asked Questions
+==========================
+
+I am getting weird Git errors about an unknown ``-C`` option
+------------------------------------------------------------
+
+You may be using a system with an old version of Git.
+You may install a recent version of Git for your user by running ``nix-env -iA nixpkgs.git``.
+
+A file I created isn’t found when I run ``nix build``
+-----------------------------------------------------
+
+If your top is a Git repository, you must ``git add`` those files to make them recognized by Nix.
+
+An App can’t find a build product from another App
+--------------------------------------------------
+
+EPNix enables parallel builds by default.
+These means that if App dependencies aren’t specified, these Apps will compile in no particular order.
+Use ``_DEPEND_DIRS += `` in your top-level ``Makefile``.
+
+How do I version a whole EPNix top?
+-----------------------------------
+
+Meaning, not versioning an App separate from the top.
+This might be justified if you don’t intend to share an App in any other top.
+
+1. First, create a top and an App, as in the :doc:`tutorials/streamdevice` tutorial.
+
+2. Make sure to add an exception for the ``exampleApp`` folder at the end of the top’s ``.gitignore`` file:
+
+.. code-block:: ini
+
+ ...
+ # Applications and Support modules should be an EPNix dependency in flake.nix
+ *App
+ *Sup
+ # You can add exceptions like this:
+ # ---
+ #!myCustomLocalApp
+ !exampleApp
+
+3. Then, version both the top and the App:
+
+.. code-block:: bash
+
+ git init
+ git add -N .
+
+4. Finally, in your ``flake.nix``, you can remove any input and value in ``epnix.applications.apps`` that refers to this directory.
diff --git a/docs/ioc/index.rst b/docs/ioc/index.rst
new file mode 100644
index 00000000..466275f3
--- /dev/null
+++ b/docs/ioc/index.rst
@@ -0,0 +1,44 @@
+Introduction
+============
+
+.. TODO: rewrite this into an introduction to the EPICS IOC part of EPNix
+
+EPNix (pronunciation: like you are high on mushrooms) provides a way of building and packaging EPICS IOCs using the `Nix`_ package manager.
+
+By leveraging the Nix package manager,
+it provides several advantages compared to packaging EPICS the traditional way:
+
+Reproducibility:
+ Your development environment is the same as your coworker’s development environment, which is the same as your production environment.
+Complete dependencies:
+ Your EPICS IOCs ship with the complete set of dependencies, which means you can to deploy your IOC without needing to install any dependency on the target machine (except for Nix itself).
+Dependency traceability:
+ The version of your dependencies are locked, updated manually, and traced in your ``flake.lock`` file.
+ Combined with code versioning, you can build your project with the same environment years later, and you can roll back to any earlier version.
+Development shell:
+ Provides you with a set of tools adapted to your project, no matter what you have installed on your machine.
+Declarative configuration:
+ Define what you want in your IOC in a declarative and extendable manner.
+Integration tests:
+ Write tests using Python, by starting a virtual machine with your IOC running.
+
+.. _Nix: https://nixos.org/guides/how-nix-works.html
+
+Packaging policy
+~~~~~~~~~~~~~~~~
+
+To be able to specify your dependencies in your EPNix configuration, EPNix provides a package repository, packaging for example ``epics-base``, ``asyn``, ``StreamDevice``, etc.
+
+In its package repository, EPNix officially supports the latest upstream version.
+
+However, since Nix “locks” your dependencies, this means you don’t need to upgrade your dependencies if you don’t want to.
+What this means in practice: your IOC repository uses the EPNix project repository at a fixed commit, like using a repository at a fixed point in time.
+Nix records this commit SHA in the ``flake.lock`` file, which should be checked out in your Git repository.
+
+The epics-base package
+^^^^^^^^^^^^^^^^^^^^^^
+
+The epics-base package has no significant modification compared to the upstream version at `Launchpad`_.
+One goal of EPNix is to keep those modifications to a minimum, and upstream what’s possible.
+
+.. _Launchpad: https://git.launchpad.net/epics-base
diff --git a/docs/ioc/tutorials/index.rst b/docs/ioc/tutorials/index.rst
new file mode 100644
index 00000000..c4b48c61
--- /dev/null
+++ b/docs/ioc/tutorials/index.rst
@@ -0,0 +1,15 @@
+Tutorials
+=========
+
+`Tutorials`_ for users of the EPNix project wanting to build and deploy EPICS IOCs.
+
+.. _Tutorials: https://diataxis.fr/tutorials/
+
+----
+
+.. toctree::
+ :maxdepth: 1
+
+ ./pre-requisites.rst
+ ./streamdevice.rst
+ ./integration-tests.rst
diff --git a/docs/ioc/tutorials/integration-tests.rst b/docs/ioc/tutorials/integration-tests.rst
new file mode 100644
index 00000000..77be658e
--- /dev/null
+++ b/docs/ioc/tutorials/integration-tests.rst
@@ -0,0 +1,141 @@
+Integration tests
+=================
+
+Writing the test
+----------------
+
+Through the `NixOS testing framework`_,
+EPNix provides a way of specifying a machine configuration,
+and running a Python script that can do various kind of testing.
+
+If you created your IOC using the EPNix template,
+like suggested in the :doc:`streamdevice` tutorial,
+you will see a ``checks/`` directory.
+This directory should contain the integration tests you want to run.
+
+To add an integration test to EPNix,
+record it in your ``flake.nix`` under the ``epnix.checks.files`` option.
+
+For example, in the EPNix template, you will see in your ``flake.nix`` file:
+
+.. code-block:: nix
+
+ checks.files = [ ./checks/simple.nix ];
+
+The ``./checks/.nix`` file should contain a NixOS test like so:
+
+.. code-block:: nix
+
+ { build, pkgs, ... }:
+
+ pkgs.nixosTest {
+ name = "myTest";
+
+ machine = {
+ # Description of the NixOS machine...
+ };
+
+ testScript = ''
+ # Python script that does the testing...
+ '';
+ }
+
+This test will create a NixOS virtual machine
+from the given configuration,
+and run the test script.
+Note that the test script does *not* run on the virtual machine,
+but communicates with it.
+This is because the test script can start,
+shut down,
+or reboot the machine,
+and also because NixOS tests can also manage several virtual machines,
+not just one.
+
+For an overview of what you can input in the machine configuration,
+please refer to the `NixOS documentation`_.
+You can also read more about it
+in the `Python test script API documentation`_.
+
+.. _NixOS testing framework: https://nixos.org/manual/nixos/stable/index.html#sec-nixos-tests
+.. _NixOS documentation: https://nixos.org/manual/nixos/stable/index.html#sec-configuration-syntax
+.. _Python test script API documentation: https://nixos.org/manual/nixos/stable/index.html#sec-nixos-tests
+
+Starting your IOC through systemd
+---------------------------------
+
+We recommend starting your IOC through a systemd service,
+which you can describe in Nix like so:
+
+.. TODO: change that
+
+.. code-block:: nix
+
+ # Inside the `machine` attribute
+ {
+ systemd.services.my-ioc = {
+ wantedBy = [ "multi-user.target" ];
+ serviceConfig = {
+ ExecStart = "${build}/iocBoot/iocexample/st.cmd";
+ WorkingDirectory = "${build}/iocBoot/iocexample";
+
+ # Makes the EPICS command-line not quit for 100 seconds, if it doesn't
+ # receive anything on the standard input
+ StandardInputText = "epicsThreadSleep(100)";
+ };
+ };
+
+ # Provides the caget / caput / etc. commands to the test script
+ environment.systemPackages = [ pkgs.epnix.epics-base ];
+ }
+
+You can view the list of options available for a NixOS machine `here `__.
+
+Then, you can write your test script.
+Note that the test script doesn’t run directly on the machine,
+but communicates with the machine through the ``machine`` variable.
+
+An example of a testing script:
+
+.. code-block:: python
+
+ start_all()
+
+ machine.wait_for_unit("default.target")
+ machine.wait_for_unit("my-ioc.service")
+
+ machine.wait_until_succeeds("caget stringin")
+ machine.wait_until_succeeds("caget stringout")
+ machine.fail("caget non-existing")
+
+ with subtest("testing stringout"):
+ def test_stringout(_) -> bool:
+ machine.succeed("caput stringout 'hello'")
+ status, _output = machine.execute("caget -t stringout | grep -qxF 'hello'")
+
+ return status == 0
+
+ retry(test_stringout)
+
+ assert "hello" not in machine.succeed("caget -t stringin")
+
+Note that the script extensively uses the ``wait_until_succeeds`` method and the ``retry`` function.
+This is because EPICS has few guarantees about whether it propagates changes immediately,
+and so it’s better to encourage the use of retries,
+instead of hoping the timing lines up.
+
+If you would like to use a fully fledged python script on the machine,
+which can use Python dependencies like pyepics,
+please refer to the guide :doc:`../user-guides/testing/packaging-python-scripts`.
+
+You can find methods available on the ``machine`` variable and other particularities in the `NixOS tests documentation`_.
+
+You can also look at examples either in the EPNix repository,
+under the `ioc/tests folder`_,
+or in nixpkgs under the `nixos/tests folder`_.
+
+.. TODO: this doesn't explain how to run the test
+
+.. _Packaging Python scripts: ../guides/testing/packaging-python-scripts.md
+.. _NixOS tests documentation: https://nixos.org/manual/nixos/stable/index.html#sec-nixos-tests
+.. _ioc/tests folder: https://github.com/epics-extensions/epnix/tree/master/ioc/tests
+.. _nixos/tests folder: https://github.com/NixOS/nixpkgs/tree/master/nixos/tests
diff --git a/docs/ioc/tutorials/pre-requisites.rst b/docs/ioc/tutorials/pre-requisites.rst
new file mode 100644
index 00000000..4fa38d15
--- /dev/null
+++ b/docs/ioc/tutorials/pre-requisites.rst
@@ -0,0 +1,80 @@
+Pre-requisites
+==============
+
+The requirements for using EPNix are having curl, Nix, and Git installed,
+either in a Linux system,
+or in Windows’ WSL2.
+Nix must be configured with “flakes” enabled.
+
+You *don’t* need to have EPICS base installed globally,
+EPNix makes it available to you
+when you enter your top’s development shell.
+
+Having a global EPICS base installation shouldn’t pose any issue.
+
+Installing Nix
+--------------
+
+.. warning::
+
+ If you use a Linux distribution with SELinux,
+ be sure to turn it off.
+ You can do this by adding the line ``SELINUX=disabled`` in ``/etc/sysconfig/selinux``
+ on distributions based on RedHat Enterprise Linux (RHEL) like CentOS, Rocky Linux, and so on.
+
+If you don’t have Nix installed,
+first follow the `official instructions`_.
+Make sure to have the ``xz`` utility installed beforehand,
+often part of the ``xzip`` or ``xz`` package.
+
+Unless you use WSL2,
+use the multi-user installation,
+because it builds packages in an isolated environment.
+
+.. _official instructions: https://nixos.org/download/#nix-install-linux
+
+Enabling Nix flakes and the ``nix`` command
+-------------------------------------------
+
+Because Nix flakes and the unified ``nix`` command are experimental features at the time of writing,
+you need to enable them in your ``/etc/nix/nix.conf``.
+
+To enable this feature,
+add this line to your ``/etc/nix/nix.conf``:
+
+.. code-block:: ini
+
+ experimental-features = nix-command flakes
+
+If you have installed Nix in multi-user mode,
+then you have to restart the Nix daemon by running:
+
+.. code-block:: bash
+
+ systemctl restart nix-daemon.service
+
+Untracked files and Nix flakes
+------------------------------
+
+One important thing with Nix flakes:
+when your flake is in a Git repository,
+Nix only considers files that Git tracks.
+
+For example,
+if your ``flake.nix`` is in a Git repository,
+and you create a file ``foobar.txt``,
+you must run ``git add [-N] foobar.txt`` to make Nix recognize it.
+
+This prevents copying build products into the Nix store.
+
+Git version
+-----------
+
+If you use an old system and see Git errors when using Nix,
+install a recent version of Git by running this:
+
+.. code-block:: bash
+
+ nix-env -iA nixpkgs.git
+
+This command installs a recent version of Git for your current user.
diff --git a/docs/ioc/tutorials/streamdevice.rst b/docs/ioc/tutorials/streamdevice.rst
new file mode 100644
index 00000000..528a7c9e
--- /dev/null
+++ b/docs/ioc/tutorials/streamdevice.rst
@@ -0,0 +1,303 @@
+Creating a StreamDevice IOC
+===========================
+
+In this tutorial,
+you’re going to learn how to create an EPICS IOC with EPNix
+that communicates with a power supply,
+using the `StreamDevice`_ support module.
+
+.. _StreamDevice: https://paulscherrerinstitute.github.io/StreamDevice/
+
+Pre-requisites
+--------------
+
+Verify that you have all pre-requisites installed.
+If not,
+follow the :doc:`./pre-requisites` section.
+
+Running the power supply simulator
+----------------------------------
+
+EPNix has a power supply simulator
+for you to test your IOC.
+
+To run it:
+
+.. code-block:: bash
+
+ nix run 'github:epics-extensions/epnix#psu-simulator'
+
+For the rest of the tutorial,
+leave it running in a separate terminal.
+
+Creating your top
+-----------------
+
+We can use these command to create an EPNix top:
+
+.. code-block:: bash
+
+ # Initialise an EPNix top
+ nix flake new -t 'github:epics-extensions/epnix' my-top
+ cd my-top
+
+ # Enter the EPNix development shell, that has EPICS base installed in it.
+ nix develop
+
+ # Create your app and ioc boot folder
+ makeBaseApp.pl -t ioc example
+ makeBaseApp.pl -i -t ioc -p example -a linux-x86_64 Example
+
+ # Create a git repository, and make sure all files are tracked
+ git init
+ git add .
+
+After that,
+you can already check that your top build with:
+
+.. code-block:: bash
+
+ nix build -L
+
+This ``nix build`` command compiles your IOC,
+and all its dependencies.
+This makes the usual EPICS environment setup unneeded.
+
+If found in the official Nix cache server,
+Nix downloads packages from there
+instead of compiling them.
+
+This command puts a ``./result`` symbolic link in your current directory,
+containing the compilation result.
+
+Adding StreamDevice to the EPNix environment
+--------------------------------------------
+
+Adding dependencies to the EPNix environment happen inside the ``flake.nix`` file.
+This file is the main entry point for specifying your build environment:
+most Nix commands used here read this file to work.
+
+For adding StreamDevice,
+change yours like so:
+
+.. code-block:: diff
+ :caption: :file:`flake.nix`
+
+ # Add one of the supported modules here:
+ # ---
+ - #support.modules = with pkgs.epnix.support; [ StreamDevice ];
+ + support.modules = with pkgs.epnix.support; [ StreamDevice ];
+
+Then,
+leave your EPNix development shell by running ``exit``,
+and re-enter it with ``nix develop``.
+
+Because you modified the support modules,
+run ``eregen-config`` to regenerate ``configure/RELEASE.local``.
+
+With this,
+your development shell has StreamDevice available,
+and StreamDevice is also added in the ``RELEASE.local`` file.
+
+.. tip::
+
+ As a rule,
+ each time you edit the ``flake.nix`` file,
+ leave and re-enter your development shell (``exit`` then ``nix develop``),
+ and run ``eregen-config``.
+
+Adding StreamDevice to your EPICS app
+-------------------------------------
+
+To add StreamDevice to your app,
+make the following modifications:
+
+Change the ``exampleApp/src/Makefile``
+so that your App knows the record types of StreamDevice and its dependencies.
+Also change that file so that it links to the StreamDevice library and its dependencies,
+during compilation.
+For example:
+
+.. code-block:: makefile
+ :caption: :file:`exampleApp/src/Makefile`
+
+ # ...
+
+ # Include dbd files from all support applications:
+ example_DBD += calc.dbd
+ example_DBD += asyn.dbd
+ example_DBD += stream.dbd
+ example_DBD += drvAsynIPPort.dbd
+
+ # Add all the support libraries needed by this IOC
+ example_LIBS += calc
+ example_LIBS += asyn
+ example_LIBS += stream
+
+ # ...
+
+Create the ``exampleApp/Db/example.proto`` file
+that has the definition of the protocol.
+This file tells StreamDevice what to send the power supply,
+and what to expect in return.
+
+.. code-block:: perl
+ :caption: :file:`exampleApp/Db/example.proto`
+
+ Terminator = LF;
+
+ getVoltage {
+ out ":VOLT?"; in "%f";
+ }
+
+ setVoltage {
+ out ":VOLT %f";
+ @init { getVoltage; }
+ }
+
+Create the ``exampleApp/Db/example.db`` file.
+That file specifies the name, type, and properties of the Process Variables (PV)
+that EPICS exposes over the network.
+It also specifies how they relate to the functions written in the protocol file.
+
+.. code-block:: perl
+ :caption: :file:`exampleApp/Db/example.db`
+
+ record(ai, "${PREFIX}VOLT-RB") {
+ field(DTYP, "stream")
+ field(INP, "@example.proto getVoltage ${PORT}")
+ }
+
+ record(ao, "${PREFIX}VOLT") {
+ field(DTYP, "stream")
+ field(OUT, "@example.proto setVoltage ${PORT}")
+ }
+
+Change ``exampleApp/Db/Makefile``
+so that the EPICS build system installs ``example.proto`` and ``example.db``:
+
+.. code-block:: makefile
+ :caption: :file:`exampleApp/Db/Makefile`
+
+ # ...
+
+ #----------------------------------------------------
+ # Create and install (or just install) into /db
+ # databases, templates, substitutions like this
+ DB += example.db
+ DB += example.proto
+
+ # ...
+
+Change your ``st.cmd`` file
+so that it knows where to load the protocol file,
+and how to connect to the remote power supply.
+
+.. code-block:: csh
+ :caption: :file:`iocBoot/iocExample/st.cmd`
+
+ #!../../bin/linux-x86_64/example
+
+ < envPaths
+
+ ## Register all support components
+ dbLoadDatabase("${TOP}/dbd/example.dbd")
+ example_registerRecordDeviceDriver(pdbbase)
+
+ # Where to find the protocol files
+ epicsEnvSet("STREAM_PROTOCOL_PATH", "${TOP}/db")
+ # The TCP/IP address of the power supply
+ drvAsynIPPortConfigure("PS1", "localhost:8727")
+
+ ## Load record instances
+ dbLoadRecords("${TOP}/db/example.db", "PREFIX=, PORT=PS1")
+
+ iocInit()
+
+And run ``chmod +x iocBoot/iocExample/st.cmd``
+so that you can run your command file as-is.
+
+You can test that your top builds by running:
+
+.. code-block:: bash
+
+ nix build -L
+
+You will see that your IOC does not build.
+This is because we haven’t told Git to track those newly added files,
+and so Nix ignores them too.
+
+Run ``git add .`` for Git and Nix to track all files,
+and try a ``nix build -L`` again.
+
+If everything goes right,
+you can examine your compiled top under ``./result``.
+
+You can observe that:
+
+- the ``example`` app is installed under ``bin/`` and ``bin/linux-x86_64``,
+ and links to the correct libraries
+- ``example.proto`` and ``example.db`` are installed under ``db/``
+- ``example.dbd`` is generated and installed under ``dbd/``
+
+Running your IOC
+----------------
+
+To run your IOC,
+build it first with ``nix build -L``,
+and change directory into the ``./result/iocBoot/iocExample`` folder.
+Then, run:
+
+.. code-block:: bash
+
+ ./st.cmd
+
+You should see the IOC starting and connecting to ``localhost:8727``.
+
+Recompiling with make
+---------------------
+
+Using ``nix build`` to compile your IOC each time might feel slow.
+This is because Nix re-compiles your IOC from scratch each time.
+
+If you want a more “traditional” edit / compile / run workflow,
+you can place yourself in the development shell with ``nix develop``,
+and use ``make`` from here.
+
+Next steps
+----------
+
+More commands are available in the power supply simulator.
+To view them,
+close your IOC,
+and open a direct connection to the simulator:
+
+.. code-block:: bash
+
+ nc localhost 8727
+ # or
+ telnet localhost 8727
+
+You can install the ``nc`` command through the ``netcat`` package,
+or you can install the ``telnet`` command through the ``telnet`` package,
+
+Either command opens a prompt
+where you can type ``help`` then press enter
+to view the available commands.
+
+Try to edit the protocol file and the database file
+to add those features to your IOC.
+
+For more information about how to write the StreamDevice protocol,
+have a look at the `Protocol Files`_ documentation.
+
+You might also be interested in reading :doc:`../user-guides/flake-registry`.
+
+.. _Protocol Files: https://paulscherrerinstitute.github.io/StreamDevice/protocol.html
+
+Pitfalls
+--------
+
+Although EPNix tries to be close to a standard EPICS development,
+some differences might lead to confusion.
+You can find more information about this in the :doc:`../faq`.
diff --git a/docs/ioc/user-guides/developing-modules.rst b/docs/ioc/user-guides/developing-modules.rst
new file mode 100644
index 00000000..31fa3afc
--- /dev/null
+++ b/docs/ioc/user-guides/developing-modules.rst
@@ -0,0 +1,99 @@
+Developing support modules
+==========================
+
+.. TODO: rewrite, not clear enough, probably move most of it into day-to-day tutorial
+
+It can happen that one needs to hack on an EPICS support module,
+while also developing an App.
+This might be to develop and test the support module,
+or to patch and test the support module, etc.
+
+This is where Nix’s reproducibility guarantees might seem to be in the way:
+dependencies are taken from the ``/nix/store`` instead of your local repository.
+You can add it as a flake input instead,
+but that requires to run ``nix flake lock --update-input mySupport`` on each modification, etc.
+
+To bypass these constraints,
+there are several mechanisms that let you to temporarily weaken these constraints for development purposes.
+
+Packaging a starter module
+--------------------------
+
+First, clone the EPNix repository, and package your support module.
+
+.. You can look at the `Packaging modules`_ (TODO) guide,
+ this doesn’t even have to compile yet,
+ but you need to specify the dependencies of your support module.
+
+ .. _Packaging modules: ../developer-guides/packaging-modules.md
+
+Hacking on your module
+----------------------
+
+From the directory containing the source code of your support module, run:
+
+.. code-block:: bash
+
+ nix develop "/path/to/local/epnix#support/mySupport"
+ # Then, inside the development shell
+ dontUnpack=true
+ genericBuild
+
+This will put the result of your compilation under ``outputs/out``.
+If you make modifications to your support module,
+run ``buildPhase`` from the same development shell to recompile it.
+
+Using it on your EPICS top
+--------------------------
+
+Before trying to compile your top,
+make sure that your support module is included in the ``support.modules`` option of your EPNix top:
+
+.. code-block:: nix
+
+ support.modules = with pkgs.epnix.support; [ mySupport ];
+
+Now that the support module is compiled and installed in a local directory,
+you can ask Nix to use it as is.
+This can be done by running this command from your EPICS top directory:
+
+.. code-block:: bash
+
+ nix develop \
+ --override-input epnix '/path/to/local/epnix' \
+ --redirect '/path/to/local/epnix#support/mySupport' '/path/to/mySupport/outputs/out'
+ # Then, normal hacking on an EPICS top...
+
+The ``--override-input`` option instructs Nix to use your local EPNix fork
+instead of the one hosted on GitHub.
+Use this option to override flake inputs.
+
+The ``--redirect`` option instructs Nix to use your local directory for your support module,
+instead of a module installed in the ``/nix/store``.
+Use this option to override individual packages.
+
+--------------
+
+With this setup,
+you can hack and compile your support module,
+and the changes will be directly visible to your top.
+This enables you to hack on both project at the same time,
+each on their own development shell.
+
+One question one may ask:
+
+ What’s the difference between
+ running the complex ``nix develop`` command
+ and just putting ``/path/to/mySupport/outputs/out`` into ``RELEASE.local``?
+
+One thing that the complex ``nix develop`` command does correctly,
+is replacing *everything* that would have been ``/nix/store/...-mySupport../`` into your development shell.
+This includes the ``RELEASE.local`` file,
+but this may not be the only thing:
+
+For example,
+if you’re hacking on the ``seq`` support module,
+not only will it put the path to your local ``seq`` module into ``RELEASE.local``,
+but it will also put some ``seq`` specific programs into your ``$PATH``, like the ``snc`` utility.
+These programs will be those from your local build,
+not the ones coming from the EPNix repository.
diff --git a/docs/ioc/user-guides/flake-registry.rst b/docs/ioc/user-guides/flake-registry.rst
new file mode 100644
index 00000000..a945a50b
--- /dev/null
+++ b/docs/ioc/user-guides/flake-registry.rst
@@ -0,0 +1,34 @@
+Setting up the flake registry
+=============================
+
+While developing with EPNix,
+it’s possible you will end up typing ``'github:epics-extensions/epnix'`` quite often.
+
+It happens when you need to create a “top” template,
+or when you just want to have ``epics-base`` in your shell,
+and so on.
+
+This is tedious.
+
+Nix provides a way of shortening these URLs,
+by adding to the `Nix registry`_:
+
+.. code-block:: bash
+
+ nix registry add epnix 'github:epics-extensions/epnix'
+
+Now, referring to ``epnix`` in Nix command-lines will be as if you referred to the full URL.
+For example, the develop command to have EPICS based installed outside of a top would be:
+
+.. code-block:: bash
+
+ nix develop epnix
+
+If you want to initialize an EPNix top,
+you can run:
+
+.. code-block:: bash
+
+ nix flake new -t epnix my-top
+
+.. _Nix registry: https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-registry.html#description
diff --git a/docs/ioc/user-guides/index.rst b/docs/ioc/user-guides/index.rst
new file mode 100644
index 00000000..4069e853
--- /dev/null
+++ b/docs/ioc/user-guides/index.rst
@@ -0,0 +1,19 @@
+User guides
+===========
+
+`How-to guides`_ for users of the EPNix project wanting to build and deploy EPICS IOCs.
+
+.. _How-to guides: https://diataxis.fr/how-to-guides/
+
+----
+
+.. toctree::
+ :maxdepth: 1
+
+ ./private-repo-setup.rst
+ ./flake-registry.rst
+ ./developing-modules.rst
+ ./testing/packaging-python-scripts.rst
+
+.. TODO: make a NixOS test user-guide,
+ and add a toctree for testing-related guides there
diff --git a/docs/ioc/user-guides/private-repo-setup.rst b/docs/ioc/user-guides/private-repo-setup.rst
new file mode 100644
index 00000000..ae953085
--- /dev/null
+++ b/docs/ioc/user-guides/private-repo-setup.rst
@@ -0,0 +1,165 @@
+Private repository setup
+========================
+
+To avoid a great deal of confusion,
+it’s best to configure your machine
+so that it can clone your private repositories unattended.
+This means that this command should succeed
+without asking for user input on the terminal:
+
+.. code-block:: bash
+
+ git clone 'ssh://git@your.gitlab.com/your/epicsApp.git'
+
+But, asking for user input graphically is acceptable.
+
+The reason is
+that the Nix command-line tool often writes text on the terminal,
+and does so over the questions asked by programs like SSH or Git.
+If SSH or Git asks for a password on the terminal,
+you probably won’t see it,
+and confusion will follow when the Nix command hangs.
+
+There are two main ways to configure your machine for this:
+
+- SSH keys
+- GitHub / GitLab tokens
+
+SSH keys
+--------
+
+Configuring the SSH key
+~~~~~~~~~~~~~~~~~~~~~~~
+
+To set up SSH keys to use with your GitHub account,
+you can follow the `GitHub SSH documentation`_.
+
+To set up SSH keys to use with your GitLab account,
+you can follow the `GitLab SSH documentation`_,
+and particularly look at these sections:
+
+- See if you have an existing SSH key pair
+- Generate an SSH key pair
+- Add an SSH key to your GitLab account
+- Verify that you can connect
+
+.. _GitHub SSH documentation: https://docs.github.com/en/authentication/connecting-to-github-with-ssh
+.. _GitLab SSH documentation: https://docs.gitlab.com/ee/user/ssh.html
+
+Configuring the ssh-agent
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you have a GNOME installation,
+chances are you already have an ssh-agent installed and running.
+
+To check,
+open a *new* terminal,
+and look at the value of the ``$SSH_AUTH_SOCK`` variable.
+
+If it returns nothing,
+install ``gnome-keyring``,
+then log out and log in again.
+
+Then reopen a new terminal,
+and add your configured SSH key like so:
+
+.. code-block:: bash
+
+ ssh-add 'path/to/key/id_ed25519'
+
+Check that logging in to GitLab doesn’t ask for user input on the terminal:
+
+.. code-block:: bash
+
+ ssh -T 'git@your.gitlab.com'
+ # Welcome to GitLab, @user!
+
+If it doesn’t work,
+but you have ``gnome-keyring-daemon`` installed and running,
+you can add this line to your ``~/.bashrc``:
+
+.. code-block:: bash
+
+ export SSH_AUTH_SOCK=/run/user/$UID/keyring/ssh
+
+GitHub / GitLab tokens
+----------------------
+
+GitHub and GitLab tokens offer a timed way of authenticating,
+suitable for either quick and dirty access,
+or for setting up services or scripts
+that need access to GitHub / GitLab repositories.
+In any case,
+tokens shouldn’t be used for usual development.
+
+In GitLab,
+you can create tokens either per-user,
+per-group,
+or per-project.
+
+In GitHub,
+you can only create personal access tokens,
+but their usage can be restricted in an organization.
+
+Creating a GitHub token,
+or a GitLab token for your user means
+that you can give it access to all projects and APIs that you have access to.
+This can be useful for your personal applications,
+or tools like `glab`_.
+
+In GitLab,
+you can create a token for given group or project
+so that it offers access to that specific group or project.
+These kinds of tokens should be preferred.
+
+.. _glab: https://docs.gitlab.com/ee/editor_extensions/gitlab_cli/index.html
+
+GitHub
+~~~~~~
+
+To create a GitHub token, follow the `GitHub token documentation`_
+
+.. _GitHub token documentation: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens
+
+GitLab
+~~~~~~
+
+.. TODO: reference official documentation
+
+To create a GitLab token,
+go to the user/group/project settings,
+under the “Access Tokens” section.
+Give your token a meaningful name,
+an expiration date,
+and for a group/project,
+select the appropriate role.
+
+If you only want your token to be able to clone repositories,
+you can just select the ``read_repository`` scope.
+Else refer to GitLab’s official documentation by clicking “Learn more.”
+
+After creating the personal access token,
+you can copy the token’s value.
+Be careful,
+as this value won’t be accessible after closing the page.
+
+If you want to clone any repository,
+the URL will need to be:
+
+.. code-block:: bash
+
+ https://gitlab-ci-token:${TOKEN}@your.gitlab.com"
+
+As EPNix uses SSH flake inputs,
+you can use this command to instruct Git to rewrite GitLab URLs:
+
+.. code-block:: bash
+
+ git config --global url."https://gitlab-ci-token:${TOKEN}@your.gitlab.com".insteadOf "ssh://git@your.gitlab.com"
+
+To check that your setup is working,
+run the following command:
+
+.. code-block:: bash
+
+ git clone 'ssh://git@your.gitlab.com/your/epicsApp.git'
diff --git a/docs/ioc/user-guides/testing/packaging-python-scripts.rst b/docs/ioc/user-guides/testing/packaging-python-scripts.rst
new file mode 100644
index 00000000..afa51fe5
--- /dev/null
+++ b/docs/ioc/user-guides/testing/packaging-python-scripts.rst
@@ -0,0 +1,200 @@
+Packaging Python scripts
+========================
+
+.. TODO: move most of it in tutorial
+
+As EPNix uses Nix,
+you can package Python scripts as helpers for your integration tests,
+by using the provided `infrastructure`_ of nixpkgs.
+In fact,
+you can package any program in any language,
+but this document focuses on Python scripts with `Poetry`_ for their simplicity and popularity.
+
+.. _infrastructure: https://nixos.org/manual/nixpkgs/stable/#python
+.. _Poetry: https://python-poetry.org/
+
+Getting started
+---------------
+
+We recommend using the Poetry package in your EPNix environment,
+through Nix, to use the same version as the one building the Python script.
+
+You can do this by adding this bit in your ``flake.nix`` file:
+
+.. code-block:: nix
+
+ epnix.devShell.packages = [
+ { package = pkg.poetry; category = "development tools"; }
+ ];
+
+Next, you can start your development shell with ``nix develop``,
+go to the directory of your test,
+and create a new project with the command:
+
+.. code-block:: bash
+
+ poetry new
+
+This will create a Python project under the ```` directory.
+Under it, you will find a ``pyproject.toml``
+where you can specify the dependencies of your script.
+For example, you can specify ``modbus`` to add the Python `modbus package`_,
+if you want to test modbus communication.
+You can remove the dependency on pytest if you won’t add unit tests to your Python script.
+
+To add an entry point to your Python code,
+you can use the ``tool.poetry.scripts`` section like so:
+
+.. code-block:: toml
+
+ [tool.poetry.scripts]
+ my_python_script = "my_python_script:main"
+
+This will add an executable named ``my_python_script``
+that will run the ``main()`` function of the ``my_python_script`` module.
+
+For more information on how to use poetry,
+please refer to the `Poetry documentation`_.
+
+Before packaging this script using Nix, it’s important to generate the lock file, and to remember to re-generate it each time you change the ``pyproject.toml`` file.
+
+You can do this with the following command:
+
+.. code-block:: bash
+
+ poetry lock
+
+Then, in your integration test file (see: :doc:`../../tutorials/integration-tests`),
+you can package it like this:
+
+.. code-block:: nix
+
+ { build, pkgs, ... }:
+
+ let
+ pythonScript = pkgs.poetry2nix.mkPoetryApplication {
+ projectDir = ./path/to/my-python-script;
+ };
+ in
+ pkgs.nixosTest {
+ name = "myTest";
+
+ # ...
+ }
+
+With this, you can use the ``pythonScript`` variable as you see fit.
+
+.. _modbus package: https://pypi.org/project/modbus/
+.. _Poetry documentation: https://python-poetry.org/docs/basic-usage/
+
+Example usage: As a one shot test script
+----------------------------------------
+
+Using a packaged Python script instead of the provided ``testScript`` has several advantages.
+It can use dependencies provided by the community (like ``modbus``, ``systemd``, etc.), and you can make it run on the running virtual machine.
+
+Python script:
+
+.. code-block:: python
+
+ import subprocess
+
+ from modbus.client import *
+
+
+ def main():
+ c = client(host='HOSTNAME')
+ modbus_values = c.read(FC=3, ADR=10, LEN=8)
+
+ for i in range(8):
+ epics_value = subprocess.run(
+ ["caget", "-t", "MyPV:" + i],
+ capture_output=True,
+ ).stdout.strip()
+
+ assert modbus_values[i] == int(epics_value), "Wrong value provided by epics"
+
+Nix test:
+
+.. code-block:: nix
+
+ { build, pkgs, ... }:
+
+ let
+ pythonScript = pkgs.poetry2nix.mkPoetryApplication {
+ projectDir = ./path/to/my-python-script;
+ };
+ in
+ pkgs.nixosTest {
+ name = "myTest";
+
+ machine = {
+ environment.systemPackages = [ pythonScript ];
+
+ # ...
+ };
+
+ testScript = ''
+ # ...
+
+ my_python_script --my-flag --my-option=3
+
+ # ...
+ '';
+ }
+
+Example usage: As a systemd service
+-----------------------------------
+
+Using a Python script as a systemd service is useful for mocking devices.
+
+.. TODO: For more information, please see the `Creating a mocking server`_ guide.
+
+Python script:
+
+.. code-block:: python
+
+ import logging
+ from logging import info
+
+
+ def main():
+ logging.basicConfig(level=logging.INFO)
+
+ while True:
+ info("doing things")
+
+ # ...
+
+Nix test:
+
+.. code-block:: nix
+
+ { build, pkgs, ... }:
+
+ let
+ pythonScript = pkgs.poetry2nix.mkPoetryApplication {
+ projectDir = ./path/to/my-python-script;
+ };
+ in
+ pkgs.nixosTest {
+ name = "myTest";
+
+ machine = {
+ systemd.services."my-python-service" = {
+ wantedBy = [ "multi-user.target" ];
+
+ serviceConfig.ExecStart = "${pythonScript}/bin/my_python_script";
+ };
+
+ # ...
+ };
+
+ testScript = ''
+ # ...
+
+ machine.wait_for_unit("my-python-service.service")
+
+ # ...
+ '';
+ }
diff --git a/doc/logo.svg b/docs/logo.svg
similarity index 100%
rename from doc/logo.svg
rename to docs/logo.svg
diff --git a/docs/nixos-services/index.rst b/docs/nixos-services/index.rst
new file mode 100644
index 00000000..dfcd6a95
--- /dev/null
+++ b/docs/nixos-services/index.rst
@@ -0,0 +1,9 @@
+Introduction
+============
+
+This section of the documentation covers the deployment and administration of EPICS-related services,
+such as the Phoebus ecosystem, the Channel Access gateway, and so on.
+
+For an introduction on how to install NixOS,
+and a guided tour into how to change NixOS options,
+follow the :doc:`tutorials/archiver-appliance` tutorial.
diff --git a/docs/nixos-services/tutorials/archiver-appliance.rst b/docs/nixos-services/tutorials/archiver-appliance.rst
new file mode 100644
index 00000000..a684ddc4
--- /dev/null
+++ b/docs/nixos-services/tutorials/archiver-appliance.rst
@@ -0,0 +1,365 @@
+Creating an Archiver Appliance instance
+=======================================
+
+In this tutorial,
+we’re going to see how to create a virtual machine that runs Archiver Appliance,
+under the NixOS Linux distribution.
+
+Installing Archiver Appliance on a physical machine is definitely possible,
+but this tutorial focuses on virtual machines for simplicity’s sake.
+
+You will need:
+
+- A virtual machine,
+- and the `NixOS ISO file`_.
+ Select the “Graphical ISO image.”
+
+.. _NixOS ISO file: https://nixos.org/download/#nixos-iso
+
+Installing NixOS
+----------------
+
+First things first,
+create your virtual machine,
+and select the ISO image that you downloaded.
+
+Then, start the virtual machine.
+
+From the booted virtual machine,
+you can follow the graphical installation process,
+and reboot once finished.
+
+You can select any desktop environment,
+or no desktop.
+This tutorial only uses the command-line.
+
+Making your configuration a flake
+---------------------------------
+
+The installation process created the ``/etc/nixos`` directory in your VM.
+This directory describes the complete configuration of your machine.
+
+EPNix is a “Nix flake”,
+which a way of managing Nix projects.
+Using Nix flakes also enables you to use Nix code outside of your repository,
+in a controlled manner.
+For more information,
+see the `Nix flake command manual`_ and the `Flake wiki page`_.
+
+To be able to import EPNix into you NixOS configuration,
+you first need to turn your NixOS configuration into a Nix flake.
+
+As root, place yourself in the ``/etc/nixos`` directory in your virtual machine.
+Create a ``flake.nix`` file under it,
+by running ``nano flake.nix``.
+Fill the file with these lines:
+
+.. code-block:: nix
+ :caption: flake.nix
+ :linenos:
+
+ {
+ description = "Configuration for running Archiver Appliance in a VM";
+
+ inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
+ inputs.epnix.url = "github:epics-extensions/EPNix/nixos-23.11";
+
+ outputs = { self, nixpkgs, epnix }: {
+ nixosConfigurations.nixos = nixpkgs.lib.nixosSystem {
+ modules = [
+ epnix.nixosModules.nixos
+
+ ./configuration.nix
+ ];
+ };
+ };
+ }
+
+Save and quit by typing :kbd:`Ctrl-x`, then :kbd:`y`, and then :kbd:`Enter`,
+and run ``nixos-rebuild test`` to test your changes.
+
+Some explanations:
+
+You can see in the ``flake.nix`` file that the flake has 2 inputs:
+``nixpkgs`` and ``epnix``,
+lines 4–5.
+
+Having the ``nixpkgs`` input enables you to use code from `Nixpkgs`_.
+This is what enables you to use all those NixOS options,
+and every package currently installed on your machine.
+For more information,
+you can read the `Nixpkgs preface`_.
+
+Having the ``epnix`` input is what’s going to enable you to use :doc:`packages from EPNix <../../pkgs/packages>`,
+such as Archiver Appliance.
+It also enables you to use :doc:`EPNix' extra NixOS options <../options>`,
+such as the options configuring Tomcat, the systemd service, the ``archappl`` user and group, MariaDB, and so on.
+
+.. _Nix flake command manual: https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake.html
+.. _Flake wiki page: https://nixos.wiki/wiki/Flakes
+.. _Nixpkgs: https://github.com/NixOS/nixpkgs
+.. _Nixpkgs preface: https://nixos.org/manual/nixpkgs/stable/#preface
+
+Configuring Archiver Appliance
+------------------------------
+
+Now for the fun part,
+actually using those EPNix options to install and configure Archiver Appliance,
+and all its dependencies.
+
+Create and edit the file ``archiver-appliance.nix`` under ``/etc/nixos``.
+For now, here are the contents:
+
+.. code-block:: nix
+ :caption: :file:`archiver-appliance.nix`
+
+ {
+ services.archiver-appliance.enable = true;
+ }
+
+In your ``flake.nix``,
+import the newly created file by adding ``./archiver-appliance.nix``,
+under ``./configuration.nix``:
+
+.. code-block:: diff
+ :caption: :file:`flake.nix`
+
+ modules = [
+ epnix.nixosModules.nixos
+
+ ./configuration.nix
+ + ./archiver-appliance.nix
+ ];
+
+If you try to test your changes by running ``nixos-rebuild test``,
+you will see a helpful error message:
+
+.. code-block:: console
+
+ root@machine /etc/nixos # nixos-rebuild test
+ error: The option `services.archiver-appliance.stores.lts.location'
+ is used but not defined.
+ (use '--show-trace' to show detailed location information)
+
+This tells you that setting the ``services.archiver-appliance.stores.lts.location`` option is mandatory,
+but we didn’t set any value.
+
+To figure out what this option is about,
+you can examine the :doc:`options reference <../options>`.
+
+The options reference gives a description for this option:
+
+ Backing directory containing the LTS.
+
+and an example:
+
+.. code-block:: nix
+
+ "/data/lts"
+
+It tells us that you need to choose where the Long Term Store (LTS) is.
+See the “Architecture” section of the `Archiver Appliance Details`_ page for what the various stores are.
+
+Because this is a test VM,
+let’s configure the LTS to a test location,
+like ``/tmp/lts``.
+You will also need to configure the location of the Medium Term Store (MTS).
+
+Here’s how to change ``archiver-appliance.nix``:
+
+.. code-block:: nix
+ :caption: :file:`archiver-appliance.nix`
+
+ {
+ services.archiver-appliance.enable = true;
+ services.archiver-appliance.stores.lts.location = "/tmp/lts";
+ services.archiver-appliance.stores.mts.location = "/tmp/mts";
+ }
+
+If you don’t want to repeat yourself,
+you can also change it like so:
+
+.. code-block:: nix
+ :caption: :file:`archiver-appliance.nix`
+
+ {
+ services.archiver-appliance = {
+ enable = true;
+ stores.lts.location = "/tmp/lts";
+ stores.mts.location = "/tmp/mts";
+ };
+ }
+
+And now,
+this should succeed:
+
+.. code-block:: console
+
+ root@machine /etc/nixos # nixos-rebuild test
+ building the system configuration...
+ activating the configuration...
+ setting up /etc...
+ reloading user units for admin...
+ setting up tmpfiles
+ reloading the following units: dbus.service
+ the following new units were started: arch-lts-ArchiverStore.mount,
+ arch-mts-ArchiverStore.mount, arch-sts-ArchiverStore.mount,
+ mysql.service, tomcat.service
+
+From the message,
+we can guess that it started the Tomcat server running Archiver Appliance,
+the MySQL (in fact, MariaDB) server,
+and mounted some partitions.
+Fantastic!
+
+You can run the ``systemctl list-units`` command to see if any systemd unit failed.
+
+In the default configuration,
+Archiver Appliance and Tomcat are configured to output logs to journald.
+You can see those logs by running:
+
+.. code-block:: bash
+
+ journalctl -xeu tomcat.service
+
+You can also see the MariaDB logs by running:
+
+.. code-block:: bash
+
+ journalctl -xeu mysql.service
+
+.. note::
+
+ Here are some details on what was done by EPNix’ ``services.archiver-appliance`` NixOS module:
+
+ - Creation of the Linux user and group ``archappl``
+ - Installation and configuration of MariaDB:
+
+ - Creation of the ``archappl`` user,
+ with UNIX socket authentication
+ - Creation of the Archiver Appliance database
+ - Creation of the `various tables`_ in that database
+ - Giving access rights to this database for the ``archappl`` user
+
+ - Installation and configuration of Tomcat:
+
+ - Installation of the WAR files of Archiver Appliance
+ - Installation of the MariaDB connector and its dependencies
+ - Configuring the MariaDB connector to authenticate to the database
+ - Logging configuration to ``journald``
+
+ - Configuring mounts so that:
+
+ - ``/arch/lts`` and ``/arch/mts`` are bind mounts to the configured locations,
+ with some added security options,
+ such as ``nodev`` and ``noexec``
+ - Mounting ``/arch/sts`` as a new ``tmpfs``
+
+Tomcat runs by default under port 8080,
+and NixOS has a firewall enabled by default.
+
+Change your ``archiver-appliance.nix``:
+
+.. code-block:: nix
+
+ {
+ services.archiver-appliance = {
+ enable = true;
+ stores.lts.location = "/tmp/lts";
+ stores.mts.location = "/tmp/mts";
+
+ # New option:
+ openFirewall = true;
+ };
+ }
+
+and run ``nixos-rebuild test``.
+It will restart ``firewall.service``,
+but configured to allow incoming connection on port 8080.
+
+Check the IP address of your VM with ``ip a``,
+and open a browser to ``http://:8080/mgmt/ui/index.html``.
+
+Finally,
+run ``nixos-rebuild switch`` to confirm your changes.
+This will apply your changes for the next reboot,
+by adding a new boot entry,
+enabling you to go back to a previous configuration.
+
+You have now configured Archiver Appliance on NixOS.
+
+.. _Archiver Appliance Details: https://slacmshankar.github.io/epicsarchiver_docs/details.html
+.. _various tables: https://github.com/slacmshankar/epicsarchiverap/blob/master/src/main/org/epics/archiverappliance/config/persistence/archappl_mysql.sql
+
+Next steps
+----------
+
+This VM configuration has some problems:
+
+- It stores the LTS and MTS in ``/tmp``,
+ which by default is cleaned on reboot
+- The size of the Short Term Store (STS) isn’t configured
+- Both “management” and “retrieval” URLs are accessible without authentication
+
+The following sections are some pointers to fix these issues.
+
+Configuring partitions
+~~~~~~~~~~~~~~~~~~~~~~
+
+If you want to change the location of the LST or MTS,
+you can change the value of the corresponding options:
+
+- ``services.archiver-appliance.stores.lts.location``
+- ``services.archiver-appliance.stores.mts.location``
+
+But these values won’t mean much if the configured directories are not backed by the appropriate hardware.
+
+As an example given by the `Archiver Appliance Details`_ page,
+section “Architecture”,
+we can have the LTS backed by a NAS or SAN,
+and the MTS backed by SSD or SAS storage.
+
+The way to do that is to configure the ``fileSystems`` NixOS option.
+See the `File Systems NixOS documentation`_ for more information.
+
+.. _File Systems NixOS documentation: https://nixos.org/manual/nixos/stable/#ch-file-systems
+
+Size of the short term store
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To configure the size of the short term store,
+use the ``services.archiver-appliance.stores.sts.size`` option.
+
+For example:
+
+.. code-block:: nix
+ :caption: :file:`archiver-appliance.nix`
+
+ {
+ services.archiver-appliance = {
+ enable = true;
+ stores.lts.location = "/tmp/lts";
+ stores.mts.location = "/tmp/mts";
+
+ openFirewall = true;
+
+ # New option:
+ stores.sts.size = "20g";
+ };
+ }
+
+See the :ref:`sts.size option ` in the reference for a more in-depth description.
+
+Restricting access
+~~~~~~~~~~~~~~~~~~
+
+Allowing access to ``mgmt`` URLs to anyone can be dangerous,
+because it allows anyone to delete and archive PVs.
+
+To restrict access,
+you can close the firewall and put an nginx server in front.
+
+You can configure the nginx server to disallow access to the URLs you want.
+You can also configure nginx to require authentication.
+
+.. TODO: make a guide including HTTPS setup
diff --git a/docs/nixos-services/tutorials/index.rst b/docs/nixos-services/tutorials/index.rst
new file mode 100644
index 00000000..6a1fd94b
--- /dev/null
+++ b/docs/nixos-services/tutorials/index.rst
@@ -0,0 +1,13 @@
+Tutorials
+=========
+
+`Tutorials`_ for users of the EPNix project wanting to deploy EPICS-related services.
+
+.. _Tutorials: https://diataxis.fr/tutorials/
+
+----
+
+.. toctree::
+ :maxdepth: 1
+
+ ./archiver-appliance.rst
diff --git a/docs/nixos-services/user-guides/ca-gateway.rst b/docs/nixos-services/user-guides/ca-gateway.rst
new file mode 100644
index 00000000..5c9a1976
--- /dev/null
+++ b/docs/nixos-services/user-guides/ca-gateway.rst
@@ -0,0 +1,205 @@
+Channel Access gateway setup
+============================
+
+The Channel Access (CA) gateway is a program
+that acts as gateway,
+which enables client from a network to access IOCs on another network.
+
+Setting up a CA gateway also enables you
+to add extra access security rules on top of IOCs.
+
+For more details and documentation about the CA PV gateway,
+you can examine the `gateway main page`_.
+
+.. include:: ./pre-requisites.rst
+
+.. _gateway main page: https://epics.anl.gov/extensions/gateway/
+
+Enabling the gateway
+--------------------
+
+To enable the gateway,
+add this to your configuration:
+
+.. code-block:: nix
+ :caption: :file:`ca-gateway.nix`
+
+ {
+ services.ca-gateway = {
+ enable = true;
+ openFirewall = true;
+ };
+ }
+
+This configuration starts the CA gateway in a ``ca-gateway.service`` systemd service.
+In this configuration,
+the gateway listens on all interface with a broadcast IP address,
+and forwards all Channel Access request.
+
+The ``openFirewall`` option opens the
+5064 TCP,
+5064 UDP,
+and 5065 UDP ports on all network interfaces.
+
+Firewall on specific interfaces
+-------------------------------
+
+If you want to enable the firewall on specific interfaces,
+you can remove the ``openFirewall`` option
+and configure the firewall manually.
+
+You can also use the ``cip`` setting
+to specify where the gateway should listen
+for the CA server part.
+
+For example:
+
+.. code-block:: nix
+ :caption: :file:`ca-gateway.nix`
+
+ {config, ...}: {
+ services.ca-gateway = {
+ enable = true;
+ # Server side listen address
+ # Let's say this IP address is on enp0s2
+ settings.sip = ["10.0.2.1"];
+
+ # Let's also say the enp0s1 interface is
+ # where communication with "real" IOCs happen (client side)
+
+ # openFirewall is left as false by default
+ };
+
+ networking.firewall = let
+ gwSettings = config.services.ca-gateway.settings;
+ in {
+ interfaces = {
+ # Open the firewall on the interface from the client side of the gateway,
+ # this will be the side of the gateway listening
+ # for replies to beacons and PV search requests
+ "enp0s1".allowedUDPPorts = [5065];
+
+ # Open the firewall on the interface from the server side of the gateway,
+ # this will be the side of the gateway listening for Channel Access requests
+ "enp0s2" = {
+ # Use the value of the `sport` setting
+ allowedTCPPorts = [gwSettings.sport];
+ allowedUDPPorts = [gwSettings.sport];
+ };
+ };
+
+ # Allow incoming UDP packets with *source* port 5064,
+ # from the client side of the gateway.
+ # This is needed to listen to CA broadcast responses
+ extraCommands = ''
+ ip46tables -A nixos-fw -p udp --sport 5064 -j nixos-fw-accept -i enp0s1
+ '';
+ };
+ }
+
+Filtering IOCs
+--------------
+
+By using the ``cip`` setting,
+you can filter which IOCs get exposed by the gateway.
+This is equivalent to setting the environment variable ``EPICS_CA_ADDR_LIST``
+and setting ``EPICS_CA_AUTO_ADDR_LIST=NO``.
+
+For example:
+
+.. code-block:: nix
+ :caption: :file:`ca-gateway.nix`
+
+ {
+ services.ca-gateway = {
+ enable = true;
+ # These IOCs get exposed by the gateway
+ settings.cip = [
+ "10.0.1.42"
+ "10.0.1.69"
+
+ # you can specify the port, too,
+ # if your IOC listens on something other than 5064
+ "10.0.1.237:5067"
+
+ # domain names also work
+ "myioc"
+ ];
+ };
+ }
+
+Filtering process variables
+---------------------------
+
+By using the ``pvlist`` setting,
+you can filter which PVs get exposed by the gateway.
+
+This option takes a file in the gateway ``pvlist`` format.
+See the `GATEWAY.pvlist`_ example on the ca-gateway repository.
+The list supports regular expressions (Perl style).
+
+
+.. _GATEWAY.pvlist: https://github.com/epics-extensions/ca-gateway/blob/master/example/GATEWAY.pvlist
+
+In the configuration
+~~~~~~~~~~~~~~~~~~~~
+
+For example:
+
+.. code-block:: nix
+ :caption: :file:`ca-gateway.nix`
+
+ {pkgs, ...}: {
+ services.ca-gateway = {
+ enable = true;
+ # These PVs get exposed by the gateway
+ # This list implements an "allowlist":
+ # DENY by default, some PVs explicitely ALLOW
+ settings.pvlist = pkgs.writeText "gateway.pvlist" ''
+ EVALUATION ORDER DENY, ALLOW
+
+ .* DENY
+
+ MY_PV1 ALLOW
+ MY_PV2 ALLOW
+
+ # Or:
+
+ MY_PV[0-9]+ ALLOW
+ '';
+ };
+ }
+
+In a separate file
+~~~~~~~~~~~~~~~~~~
+
+For long lists,
+it can be better
+to put it in a separate file.
+You can do this
+by adding a ``gateway.pvlist`` in the same directory as your configuration:
+
+.. code-block:: perl
+
+ EVALUATION ORDER DENY, ALLOW
+
+ .* DENY
+
+ MY_PV1 ALLOW
+ MY_PV2 ALLOW
+
+ # Or:
+
+ MY_PV[0-9]+ ALLOW
+
+And in your configuration:
+
+.. code-block:: nix
+
+ {
+ services.ca-gateway = {
+ enable = true;
+ # Make sure that the value is *not* quoted
+ settings.pvlist = ./gateway.pvlist;
+ };
+ }
diff --git a/docs/nixos-services/user-guides/index.rst b/docs/nixos-services/user-guides/index.rst
new file mode 100644
index 00000000..2aa264af
--- /dev/null
+++ b/docs/nixos-services/user-guides/index.rst
@@ -0,0 +1,15 @@
+User guides
+===========
+
+`How-to guides`_ for users of the EPNix project wanting to deploy EPICS-related services.
+
+.. _How-to guides: https://diataxis.fr/how-to-guides/
+
+----
+
+.. toctree::
+ :maxdepth: 1
+
+ ./ca-gateway.rst
+ ./phoebus-alarm.rst
+ ./phoebus-save-and-restore.rst
diff --git a/docs/nixos-services/user-guides/phoebus-alarm.rst b/docs/nixos-services/user-guides/phoebus-alarm.rst
new file mode 100644
index 00000000..48a2d7c9
--- /dev/null
+++ b/docs/nixos-services/user-guides/phoebus-alarm.rst
@@ -0,0 +1,223 @@
+Phoebus Alarm single server setup
+=================================
+
+The Phoebus Alarm collection of services enables monitoring EPICS PVs,
+and report alarms in a server.
+Phoebus clients can then contact this server,
+to see a list of current alarms, earlier alarms, and so on.
+
+This guide focuses on installing and configuring these services on a single server.
+
+For more information about these services,
+examine the official documentation:
+
+- `Service Architecture`_
+- `Alarm Server`_
+- `the README of Alarm Server`_ for reference only, don’t follow this guide on NixOS
+- `Alarm Logging Service`_
+
+The Phoebus Alarm Logging Service can also be called the Phoebus Alarm Logger.
+
+.. include:: ./pre-requisites.rst
+
+.. _Service Architecture: https://control-system-studio.readthedocs.io/en/latest/services_architecture.html
+.. _Alarm Server: https://control-system-studio.readthedocs.io/en/latest/services/alarm-server/doc/index.html
+.. _the README of Alarm Server: https://github.com/ControlSystemStudio/phoebus/blob/master/app/alarm/Readme.md
+.. _Alarm Logging Service: https://control-system-studio.readthedocs.io/en/latest/services/alarm-logger/doc/index.html
+
+Single server Phoebus Alarm setup
+---------------------------------
+
+To configure Phoebus Alarm, Phoebus Alarm Logger, Apache Kafka, and ElasticSearch on a single server,
+add this to your configuration,
+while taking care of replacing the IP address
+and Kafka’s ``clusterId``:
+
+.. code-block:: nix
+ :caption: :file:`phoebus-alarm.nix`
+
+ {lib, pkgs, ...}: let
+ # Replace this with your machine's external IP address
+ # or DNS domain name
+ ip = "192.168.1.42";
+ kafkaListenSockAddr = "${ip}:9092";
+ kafkaControllerListenSockAddr = "${ip}:9093";
+ in {
+ # The Phoebus Alarm server also automatically enables the Phoebus Alarm Logger
+ services.phoebus-alarm-server = {
+ enable = true;
+ openFirewall = true;
+ settings."org.phoebus.applications.alarm/server" = kafkaListenSockAddr;
+ };
+
+ services.phoebus-alarm-logger.settings."bootstrap.servers" = kafkaListenSockAddr;
+
+ # Single-server Kafka setup
+ services.apache-kafka = {
+ enable = true;
+ # Replace with a randomly generated uuid. You can get one by running:
+ # nix shell 'nixpkgs#apacheKafka' -c kafka-storage.sh random-uuid
+ clusterId = "xxxxxxxxxxxxxxxxxxxxxx";
+ formatLogDirs = true;
+ settings = {
+ listeners = [
+ "PLAINTEXT://${kafkaListenSockAddr}"
+ "CONTROLLER://${kafkaControllerListenSockAddr}"
+ ];
+ # Adapt depending on your security constraints
+ "listener.security.protocol.map" = [
+ "PLAINTEXT:PLAINTEXT"
+ "CONTROLLER:PLAINTEXT"
+ ];
+ "controller.quorum.voters" = [
+ "1@${kafkaControllerListenSockAddr}"
+ ];
+ "controller.listener.names" = ["CONTROLLER"];
+
+ "node.id" = 1;
+ "process.roles" = ["broker" "controller"];
+
+ "log.dirs" = ["/var/lib/apache-kafka"];
+ "offsets.topic.replication.factor" = 1;
+ "transaction.state.log.replication.factor" = 1;
+ "transaction.state.log.min.isr" = 1;
+ };
+ };
+
+ systemd.services.apache-kafka.unitConfig.StateDirectory = "apache-kafka";
+
+ # Open kafka to the outside world
+ networking.firewall.allowedTCPPorts = [9092];
+
+ services.elasticsearch = {
+ enable = true;
+ package = pkgs.elasticsearch7;
+ };
+
+ # Elasticsearch, needed by Phoebus Alarm Logger, is not free software (SSPL | Elastic License).
+ # To accept the license, add the code below:
+ nixpkgs.config.allowUnfreePredicate = pkg:
+ builtins.elem (lib.getName pkg) [
+ "elasticsearch"
+ ];
+ }
+
+From the Phoebus graphical client side,
+add this configuration:
+
+.. code-block:: ini
+ :caption: :file:`phoebus-client-settings.ini`
+
+ # For the Phoebus Alarm Server:
+ # Replace the IP address with your server's IP address or DNS domain name
+ org.phoebus.applications.alarm/server=192.168.1.42:9092
+
+ # For the Phoebus Alarm Logger:
+ # Replace the IP address again
+ org.phoebus.applications.alarm.logging.ui/service_uri=http://192.168.1.42:8080
+
+Configuring topics
+------------------
+
+The Phoebus Alarm system uses “topics” as a way of grouping alarms.
+These topics are the available roots of your alarm tree.
+You need to synchronize the topic names between:
+
+- Phoebus Alarm Server
+- Phoebus Alarm Logger
+- Phoebus graphical clients
+
+Changing the topic names in the Phoebus Alarm Server NixOS modules automatically creates them.
+
+.. warning::
+
+ Currently, the Phoebus Alarm Server doesn’t support several topics.
+
+For example,
+if you want to have the topic ``Project``,
+add this configuration to the server:
+
+.. code-block:: nix
+ :caption: :file:`phoebus-alarm.nix`
+
+ {config, lib, ...}: let
+ topics = ["Project"];
+ in {
+ services.phoebus-alarm-server = {
+ # ...
+ settings = {
+ # ...
+ "org.phoebus.applications.alarm/config_names" = topics;
+ };
+ };
+
+ services.phoebus-alarm-logger.settings.alarm_topics = topics;
+ }
+
+For the Phoebus graphical client,
+add this configuration:
+
+.. code-block:: ini
+ :caption: :file:`phoebus-client-settings.ini`
+
+ # config_name is only used in the Phoebus graphical client
+ org.phoebus.applications.alarm/config_name = Project
+ org.phoebus.applications.alarm/config_names = Project
+
+Configuring the address list
+----------------------------
+
+If you want to limit the IOCs reachable by the Phoebus Alarm Server,
+use these option:
+
+.. code-block:: nix
+ :caption: :file:`phoebus-alarm.nix`
+
+ {
+ services.phoebus-alarm-server = {
+ # ...
+ settings = {
+ # ...
+
+ # The Phoebus Alarm Server will only have access to these IOCs
+ "org.phoebus.pv.ca/addr_list" = ["192.168.1.5" "192.168.1.42"];
+ "org.phoebus.pv.ca/auto_addr_list" = false;
+ };
+ };
+ }
+
+Configuring email support
+-------------------------
+
+To enable email support,
+set the ``org.phoebus.email/mailport`` setting.
+Here is a list of options you might want to set:
+
+.. code-block:: nix
+ :caption: :file:`phoebus-alarm.nix`
+
+ {
+ services.phoebus-alarm-server = {
+ # ...
+ settings = {
+ # ...
+
+ "org.phoebus.email/mailhost" = "smtp.my-company.org";
+
+ # Optional:
+
+ # 25 for plain SMTP
+ "org.phoebus.email/mailport" = 25;
+ # If authentication is needed:
+ "org.phoebus.email/username" = "user";
+ "org.phoebus.email/password" = "password";
+ # Default address to be used for From:
+ # if unspecified, then the last used "from" address is used
+ "org.phoebus.email/from" = "Sender ";
+ };
+ };
+ }
+
+.. warning::
+
+ Currently, Phoebus Alarm Server only supports plain SMTP.
diff --git a/docs/nixos-services/user-guides/phoebus-save-and-restore.rst b/docs/nixos-services/user-guides/phoebus-save-and-restore.rst
new file mode 100644
index 00000000..c48bcbd6
--- /dev/null
+++ b/docs/nixos-services/user-guides/phoebus-save-and-restore.rst
@@ -0,0 +1,52 @@
+Phoebus Save-and-restore setup
+==============================
+
+The Phoebus Save-and-restore service is used by clients
+to manage configuration and snapshots of PV values.
+These snapshots can then be used by clients for comparison or for restoring PVs.
+
+This guide focuses on installing and configuring the Save-and-Restore service on a single server.
+
+For more details and documentation about Phoebus Save-and-Restore,
+you can examine the `Save-and-restore official documentation`_.
+
+.. include:: ./pre-requisites.rst
+
+.. _Save-and-restore official documentation: https://control-system-studio.readthedocs.io/en/latest/services/save-and-restore/doc/index.html
+
+Enabling the Phoebus Save-and-restore service
+---------------------------------------------
+
+To enable the Phoebus Save-and-restore service,
+add this to your configuration:
+
+.. code-block:: nix
+ :caption: :file:`phoebus-save-and-restore.nix`
+
+ {lib, ...}: {
+ services.phoebus-save-and-restore = {
+ enable = true;
+ openFirewall = true;
+ };
+
+ # Elasticsearch, needed by Phoebus Save-and-restore, is not free software (SSPL | Elastic License).
+ # To accept the license, add the code below:
+ nixpkgs.config.allowUnfreePredicate = pkg:
+ builtins.elem (lib.getName pkg) [
+ "elasticsearch"
+ ];
+ }
+
+From the Phoebus graphical client side,
+add this configuration
+
+.. code-block:: ini
+ :caption: :file:`phoebus-client-settings.ini`
+
+ # Replace the IP address with your server's IP address or domain name
+ org.phoebus.applications.saveandrestore/jmasar.service.url=http://192.168.1.42:8080
+
+.. warning::
+
+ URLs for future versions of Phoebus Save-and-restore will need to change to:
+ ``http://192.168.1.42:8080/save-restore``
diff --git a/docs/nixos-services/user-guides/pre-requisites.rst b/docs/nixos-services/user-guides/pre-requisites.rst
new file mode 100644
index 00000000..00ac0bed
--- /dev/null
+++ b/docs/nixos-services/user-guides/pre-requisites.rst
@@ -0,0 +1,41 @@
+Pre-requisites
+--------------
+
+- Having a NixOS machine with a flake configuration.
+
+If you’re not sure how to do this,
+you can follow the :doc:`../tutorials/archiver-appliance` tutorial,
+which is a good introduction on how to make a NixOS VM.
+
+If you have such a configuration,
+make sure that:
+
+- You have the ``epnix`` flake input
+- You have added ``epnix`` as an argument to your flake outputs
+- You have imported EPNix’ NixOS module
+
+For example:
+
+.. code-block:: diff
+ :caption: :file:`flake.nix`
+
+ {
+ # ...
+ inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
+ + inputs.epnix.url = "github:epics-extensions/EPNix/nixos-23.11";
+
+ # ...
+ outputs = {
+ self,
+ nixpkgs,
+ + epnix,
+ }: {
+ nixosConfigurations.nixos = nixpkgs.lib.nixosSystem {
+ modules = [
+ + epnix.nixosModules.nixos
+
+ # ...
+ ];
+ };
+ };
+ }
diff --git a/docs/release-notes/2405.rst b/docs/release-notes/2405.rst
new file mode 100644
index 00000000..6c8cc205
--- /dev/null
+++ b/docs/release-notes/2405.rst
@@ -0,0 +1,11 @@
+24.05 Release notes
+===================
+
+.. role:: nix(code)
+ :language: nix
+
+Breaking changes
+----------------
+
+- The :nix:`config.epnix.outputs.mdbook` and :nix:`config.epnix.outputs.manpages` options
+ from the IOC module options, previously deprecated, are now removed.
diff --git a/flake.nix b/flake.nix
index 63c68160..b7b8b19c 100644
--- a/flake.nix
+++ b/flake.nix
@@ -55,15 +55,14 @@
package = pkgs.poetry;
category = "development tools";
}
- {
- package = pkgs.quartoMinimal;
- category = "development tools";
- }
{
package = pkgs.vale;
category = "development tools";
}
];
+ devShell.attrs.inputsFrom = [
+ pkgs.epnix.docs
+ ];
};
};
diff --git a/ioc/modules/devshell.nix b/ioc/modules/devshell.nix
index 3a8716da..a8d4962c 100644
--- a/ioc/modules/devshell.nix
+++ b/ioc/modules/devshell.nix
@@ -212,7 +212,7 @@ in {
The value of each variable can be either a string or a list of strings.
The latter is concatenated, interspersed with colon characters.
- If null is given, the environment variable is explicitely unset,
+ If null is given, the environment variable is explicitly unset,
preventing said environment variable to "leak" from the host
environment to the development shell.
'';
@@ -320,7 +320,7 @@ in {
edoc = {
text = ''
- ${pkgs.python3}/bin/python -m http.server --bind 127.0.0.1 --directory "${pkgs.epnix.book}"
+ ${pkgs.python3}/bin/python -m http.server --bind 127.0.0.1 --directory "${pkgs.epnix.docs}/share/doc/epnix/html/"
'';
category = "epnix commands";
description = "Show the EPNix documentation book for the distribution";
@@ -572,7 +572,7 @@ in {
(map (cmd: cmd.package) cfg.packages)
++ scriptPackages
++ config.epnix.outputs.build.depsBuildBuild
- ++ [pkgs.epnix.manpages];
+ ++ [pkgs.epnix.docs];
inherit (config.epnix.outputs.build) local_config_site local_release;
diff --git a/ioc/modules/documentation.nix b/ioc/modules/documentation.nix
deleted file mode 100644
index bf45b0ca..00000000
--- a/ioc/modules/documentation.nix
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- lib,
- pkgs,
- ...
-}: {
- config.epnix.outputs = {
- mdbook =
- lib.warn
- "the mdbook output is deprecated, please use `pkgs.epnix.book` instead"
- pkgs.epnix.book;
- manpage =
- lib.warn
- "the manpage output is deprecated, please use `pkgs.epnix.manpages` instead"
- pkgs.epnix.manpages;
- };
-}
diff --git a/ioc/modules/module-list.nix b/ioc/modules/module-list.nix
index 0de3b556..a27f5f90 100644
--- a/ioc/modules/module-list.nix
+++ b/ioc/modules/module-list.nix
@@ -5,7 +5,6 @@
./checks.nix
./common.nix
./devshell.nix
- ./documentation.nix
./meta.nix
./nixos-integration.nix
./outputs.nix
diff --git a/lib/documentation.nix b/lib/documentation.nix
index f3a6e1b0..0c169eea 100644
--- a/lib/documentation.nix
+++ b/lib/documentation.nix
@@ -95,14 +95,10 @@
${lib.optionalString (pkg.meta ? position) (let
filePath = lib.head (lib.splitString ":" pkg.meta.position);
- relativePath = lib.pipe filePath [
- (lib.splitString "/")
- (lib.sublist 4 255)
- (lib.concatStringsSep "/")
- ];
+ declarationLink = self.markdown.sourceLink filePath;
in ''
Declared in
- : ${self.markdown.inDefList "[${relativePath}](file://${filePath})"}
+ : ${self.markdown.inDefList declarationLink}
'')}
License(s)
@@ -113,7 +109,6 @@
${lib.optionalString
((lib.length pkg.meta.maintainers) > 1)
" - [Mail to all maintainers](mailto:${lib.concatStringsSep "," (map (m: m.email) pkg.meta.maintainers)})"}
-
'';
};
in
diff --git a/lib/documentation/markdown.nix b/lib/documentation/markdown.nix
index 0cfbe459..fe32167f 100644
--- a/lib/documentation/markdown.nix
+++ b/lib/documentation/markdown.nix
@@ -1,5 +1,11 @@
-{lib, ...}:
+{
+ inputs,
+ lib,
+ ...
+}:
lib.fix (self: let
+ rev = inputs.self.sourceInfo.rev or "master";
+
# Quote an option if it contains a "." in it
maybeQuote = el:
if lib.hasInfix "." el
@@ -8,6 +14,7 @@ lib.fix (self: let
# Add a suggested word break after each "." so that it is easier to read
wordBreakOption = loc: lib.concatStringsSep "." (map maybeQuote loc);
+ optionName = loc: lib.concatStringsSep "." (map maybeQuote loc);
isLiteral = value:
value
@@ -40,15 +47,25 @@ in {
# ''
inDefList = str: let
lines = lib.splitString "\n" str;
- firstLine = " ${lib.head lines}";
- otherLines = map (line: " ${line}") (lib.drop 1 lines);
+ firstLine = "${lib.head lines}";
+ otherLines = map (line: " ${line}") (lib.drop 1 lines);
in
lib.concatStringsSep "\n" ([firstLine] ++ otherLines);
+ # Takes an absolute path, returns a source:// markdown link
+ sourceLink = path: let
+ relativePath = lib.pipe path [
+ (lib.splitString "/")
+ (lib.sublist 4 255)
+ (lib.concatStringsSep "/")
+ ];
+ in "[${relativePath}](source://${rev}/${relativePath})";
+
fromOption = headingLevel: option: let
header = lib.fixedWidthString headingLevel "#" "";
in ''
- ${header} ${wordBreakOption (map lib.escapeXML option.loc)}
+ (opt-${optionName option.loc})=
+ ${header} `${optionName option.loc}`
${self.toText option.description}
@@ -74,7 +91,7 @@ in {
''}
Declared in
- : ${self.inDefList (lib.concatStringsSep "\n" (map (decl: "- ${decl}") option.declarations))}
+ : ${self.inDefList (lib.concatStringsSep "\n" (map self.sourceLink option.declarations))}
'';
})
diff --git a/lib/documentation/options.nix b/lib/documentation/options.nix
index ffd86822..c6d95ee1 100644
--- a/lib/documentation/options.nix
+++ b/lib/documentation/options.nix
@@ -5,7 +5,7 @@
...
}:
lib.fix (self: {
- visibleOptionDocs = lib.filter (opt: opt.visible && !opt.internal);
+ visibleOptionDocs = lib.filter (opt: opt.visible && !opt.internal && (lib.head opt.loc) != "_module");
optionsContent = options: headingLevel:
lib.concatStringsSep "\n"
diff --git a/pkgs/default.nix b/pkgs/default.nix
index c7260268..dc24538e 100644
--- a/pkgs/default.nix
+++ b/pkgs/default.nix
@@ -84,8 +84,7 @@ in
mariadb_jdbc = callPackage ./epnix/tools/mariadb_jdbc {};
# EPNix specific packages
- book = callPackage ./book {};
- manpages = callPackage ./manpages {};
+ docs = callPackage ./docs {};
# Documentation support packages
psu-simulator = callPackage ./doc-support/psu-simulator {};
diff --git a/pkgs/book/default.nix b/pkgs/docs/default.nix
similarity index 61%
rename from pkgs/book/default.nix
rename to pkgs/docs/default.nix
index 23fcc96d..c893be01 100644
--- a/pkgs/book/default.nix
+++ b/pkgs/docs/default.nix
@@ -1,10 +1,11 @@
{
stdenvNoCC,
lib,
- epnixLib,
epnix,
- quartoMinimal,
+ epnixLib,
writeText,
+ python3,
+ installShellFiles,
documentedEpnixPkgs ? epnix,
iocConfig ? {},
nixosConfig ? {},
@@ -14,79 +15,92 @@
iocOptions = documentation.options.iocOptions iocConfig;
nixosOptions = documentation.options.nixosOptions nixosConfig;
- iocOptionsContent = documentation.options.optionsContent iocOptions 1;
+ iocOptionsContent = documentation.options.optionsContent iocOptions 3;
+ # Have a separate "Options" header for the Sphinx manpage output
iocOptionsPandoc = ''
- ---
- title: Options
- format:
- html:
- # Disable the smart extensions so that the quotes in option names are not replaced
- from: markdown-smart
- ---
+ IOC options reference
+ =====================
+
+ Options
+ -------
${iocOptionsContent}
'';
- nixosOptionsContent = documentation.options.optionsContent nixosOptions 1;
+ nixosOptionsContent = documentation.options.optionsContent nixosOptions 3;
nixosOptionsPandoc = ''
- ---
- title: NixOS Options
- format:
- html:
- # Disable the smart extensions so that the quotes in option names are not replaced
- from: markdown-smart
- ---
+ NixOS options reference
+ =======================
+
+ Options
+ -------
${nixosOptionsContent}
'';
iocPkgsListPandoc = ''
- ---
- title: Packages list
- ---
+ IOC packages list
+ =================
- ::: callout-note
+ ::: note
This page references all EPNix packages that should be used when packaging an IOC.
For all other packages, see the [Packages list](../../pkgs/packages.md).
:::
- ${epnixLib.documentation.iocPkgsList 1 documentedEpnixPkgs}
+ Packages
+ --------
+
+ ${epnixLib.documentation.iocPkgsList 3 documentedEpnixPkgs}
'';
pkgsListPandoc = ''
- ---
- title: Packages list
- ---
+ Packages list
+ =============
- ::: callout-note
+ ::: note
This page references all EPNix packages that may be used outside of an IOC.
For all IOC-specific packages, see the [IOC packages list](../ioc/references/packages.md).
:::
- ${epnixLib.documentation.pkgsList 1 documentedEpnixPkgs}
+ Packages
+ --------
+
+ ${epnixLib.documentation.pkgsList 3 documentedEpnixPkgs}
'';
in
stdenvNoCC.mkDerivation {
- name = "epnix-book";
- src = ../../doc;
-
- nativeBuildInputs = [quartoMinimal];
+ pname = "epnix-docs";
+ version = "23.11";
+
+ src = ../../docs;
+
+ nativeBuildInputs =
+ (with python3.pkgs; [
+ furo
+ myst-parser
+ sphinx
+ sphinx-copybutton
+ ])
+ ++ [
+ installShellFiles
+ ];
dontConfigure = true;
- buildPhase = ''
- runHook preBuild
-
- export HOME=$PWD
-
+ postPatch = ''
mkdir ioc/references
+ mkdir pkgs
cp "${writeText "ioc-options.md" iocOptionsPandoc}" ioc/references/options.md
cp "${writeText "ioc-packages.md" iocPkgsListPandoc}" ioc/references/packages.md
- cp "${writeText "nixos-options.md" nixosOptionsPandoc}" nixos/options.md
+ cp "${writeText "nixos-options.md" nixosOptionsPandoc}" nixos-services/options.md
cp "${writeText "packages.md" pkgsListPandoc}" pkgs/packages.md
+ '';
- quarto render
+ buildPhase = ''
+ runHook preBuild
+
+ make html man
runHook postBuild
'';
@@ -94,16 +108,20 @@ in
installPhase = ''
runHook preInstall
- cp -r _site/ "$out"
+ mkdir -p $out/bin
+ mkdir -p $out/share/doc/epnix/
+
+ cp -r _build/html $out/share/doc/epnix/
+ installManPage _build/man/*.?
runHook postInstall
'';
meta = {
- description = "The EPNix documentation book";
+ description = "The EPNix documentation";
homepage = "https://epics-extensions.github.io/EPNix/";
license = lib.licenses.asl20;
maintainers = with epnixLib.maintainers; [minijackson];
- hidden = true;
+ # hidden = true;
};
}
diff --git a/pkgs/manpages/default.nix b/pkgs/manpages/default.nix
deleted file mode 100644
index 0027c55c..00000000
--- a/pkgs/manpages/default.nix
+++ /dev/null
@@ -1,117 +0,0 @@
-{
- stdenvNoCC,
- lib,
- epnix,
- epnixLib,
- emptyDirectory,
- writeText,
- pandoc,
- documentedEpnixPkgs ? epnix,
- iocConfig ? {},
- nixosConfig ? {},
-}: let
- inherit (epnixLib) documentation;
-
- iocOptions = documentation.options.iocOptions iocConfig;
- nixosOptions = documentation.options.nixosOptions nixosConfig;
-
- iocOptionsContent = documentation.options.optionsContent iocOptions 2;
- iocPandoc = ''
- ---
- title: epnix-ioc
- section: 5
- header: EPNix IOC options and packages
- ---
-
- # PACKAGES
-
- This section references all EPNix packages that should be used when packaging an IOC.
- For all other packages, see `epnix-packages(5)`.
-
- ${epnixLib.documentation.iocPkgsList 2 documentedEpnixPkgs}
-
- ---
-
- # OPTIONS
-
- ${iocOptionsContent}
-
- # SEE ALSO
-
- `epnix-nixos(5)`, `epnix-packages(5)`
- '';
-
- nixosOptionsContent = documentation.options.optionsContent nixosOptions 2;
- nixosOptionsPandoc = ''
- ---
- title: epnix-nixos
- section: 5
- header: EPNix NixOS options
- ---
-
- # OPTIONS
-
- ${nixosOptionsContent}
-
- # SEE ALSO
-
- `epnix-ioc(5)`, `epnix-packages(5)`
- '';
-
- pkgsListPandoc = ''
- ---
- title: epnix-packages
- section: 5
- header: EPNix packages
- ---
-
- # DESCRIPTION
-
- This page references all EPNix packages that may be used outside of an IOC.
- For all IOC-specific packages, see `epnix-ioc(5)`.
-
- # PACKAGES
-
- ${epnixLib.documentation.pkgsList 2 documentedEpnixPkgs}
-
- # SEE ALSO
-
- `epnix-ioc(5)`, `epnix-nixos(5)`
- '';
-in
- stdenvNoCC.mkDerivation {
- name = "epnix-manpages";
- src = emptyDirectory;
-
- nativeBuildInputs = [pandoc];
-
- dontConfigure = true;
-
- buildPhase = ''
- runHook preBuild
-
- pandoc "${writeText "ioc-options.md" iocPandoc}" -t man -so epnix-ioc.5
- pandoc "${writeText "nixos-options.md" nixosOptionsPandoc}" -t man -so epnix-nixos.5
- pandoc "${writeText "epnix-packages.md" pkgsListPandoc}" -t man -so epnix-packages.5
-
- runHook postBuild
- '';
-
- installPhase = ''
- runHook preInstall
-
- install -Dt $out/share/man/man5/ epnix-ioc.5 epnix-nixos.5 epnix-packages.5
- # Add the bin folder so that the man path gets added to `manpath`
- mkdir -p $out/bin
-
- runHook postInstall
- '';
-
- meta = {
- description = "The EPNix documentation man page";
- homepage = "https://epics-extensions.github.io/EPNix/";
- license = lib.licenses.asl20;
- maintainers = with epnixLib.maintainers; [minijackson];
- hidden = true;
- };
- }