summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/configuration/config-syntax.chapter.md1
-rw-r--r--nixos/doc/manual/configuration/summary.section.md46
-rw-r--r--nixos/doc/manual/configuration/user-mgmt.chapter.md3
-rw-r--r--nixos/doc/manual/from_md/configuration/config-syntax.chapter.xml1
-rw-r--r--nixos/doc/manual/from_md/configuration/summary.section.xml332
-rw-r--r--nixos/doc/manual/from_md/configuration/user-mgmt.chapter.xml2
-rw-r--r--nixos/doc/manual/from_md/installation/installing.chapter.xml14
-rw-r--r--nixos/doc/manual/from_md/release-notes/rl-2211.section.xml195
-rw-r--r--nixos/doc/manual/installation/installing.chapter.md12
-rw-r--r--nixos/doc/manual/release-notes/rl-2211.section.md74
-rw-r--r--nixos/lib/test-driver/test_driver/machine.py9
-rw-r--r--nixos/modules/config/users-groups.nix22
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares.nix2
-rw-r--r--nixos/modules/module-list.nix2
-rw-r--r--nixos/modules/programs/steam.nix33
-rw-r--r--nixos/modules/services/databases/surrealdb.nix79
-rw-r--r--nixos/modules/services/desktops/gnome/at-spi2-core.nix5
-rw-r--r--nixos/modules/services/hardware/udev.nix9
-rw-r--r--nixos/modules/services/matrix/mautrix-telegram.nix29
-rw-r--r--nixos/modules/services/monitoring/grafana.nix278
-rw-r--r--nixos/modules/services/monitoring/tremor-rs.nix129
-rw-r--r--nixos/modules/services/networking/firewall.nix2
-rw-r--r--nixos/modules/services/networking/mullvad-vpn.nix19
-rw-r--r--nixos/modules/services/networking/tailscale.nix6
-rw-r--r--nixos/modules/services/system/dbus.nix72
-rw-r--r--nixos/modules/services/web-apps/alps.nix52
-rw-r--r--nixos/modules/services/web-apps/keycloak.nix27
-rw-r--r--nixos/modules/services/web-apps/mastodon.nix2
-rw-r--r--nixos/modules/services/web-apps/nextcloud.nix58
-rw-r--r--nixos/modules/services/web-apps/nextcloud.xml14
-rw-r--r--nixos/modules/services/x11/desktop-managers/plasma5.nix6
-rw-r--r--nixos/modules/system/boot/stage-1-init.sh10
-rw-r--r--nixos/modules/tasks/filesystems.nix2
-rw-r--r--nixos/modules/tasks/filesystems/ext.nix3
-rw-r--r--nixos/release-combined.nix1
-rw-r--r--nixos/tests/all-tests.nix5
-rw-r--r--nixos/tests/alps.nix104
-rw-r--r--nixos/tests/common/acme/server/acme.test.cert.pem32
-rw-r--r--nixos/tests/common/acme/server/acme.test.key.pem50
-rw-r--r--nixos/tests/common/acme/server/ca.cert.pem34
-rw-r--r--nixos/tests/common/acme/server/ca.key.pem50
-rw-r--r--nixos/tests/deluge.nix4
-rw-r--r--nixos/tests/grafana/provision/default.nix104
-rw-r--r--nixos/tests/hibernate.nix5
-rw-r--r--nixos/tests/installer-systemd-stage-1.nix7
-rw-r--r--nixos/tests/installer.nix21
-rw-r--r--nixos/tests/keycloak.nix17
-rw-r--r--nixos/tests/nextcloud/basic.nix7
-rw-r--r--nixos/tests/nextcloud/default.nix4
-rw-r--r--nixos/tests/nextcloud/openssl-sse.nix105
-rw-r--r--nixos/tests/phosh.nix65
-rw-r--r--nixos/tests/systemd-initrd-luks-password.nix5
-rw-r--r--nixos/tests/web-apps/mastodon.nix5
53 files changed, 1396 insertions, 779 deletions
diff --git a/nixos/doc/manual/configuration/config-syntax.chapter.md b/nixos/doc/manual/configuration/config-syntax.chapter.md
index 56d093c0f6e84..9f8d45d588997 100644
--- a/nixos/doc/manual/configuration/config-syntax.chapter.md
+++ b/nixos/doc/manual/configuration/config-syntax.chapter.md
@@ -15,5 +15,4 @@ NixOS configuration files.
 <xi:include href="config-file.section.xml" />
 <xi:include href="abstractions.section.xml" />
 <xi:include href="modularity.section.xml" />
-<xi:include href="summary.section.xml" />
 ```
diff --git a/nixos/doc/manual/configuration/summary.section.md b/nixos/doc/manual/configuration/summary.section.md
deleted file mode 100644
index 8abbbe257fd90..0000000000000
--- a/nixos/doc/manual/configuration/summary.section.md
+++ /dev/null
@@ -1,46 +0,0 @@
-# Syntax Summary {#sec-nix-syntax-summary}
-
-Below is a summary of the most important syntactic constructs in the Nix
-expression language. It's not complete. In particular, there are many
-other built-in functions. See the [Nix
-manual](https://nixos.org/nix/manual/#chap-writing-nix-expressions) for
-the rest.
-
-| Example                                       | Description                                                                                                        |
-|-----------------------------------------------|--------------------------------------------------------------------------------------------------------------------|
-| *Basic values*                                |                                                                                                                    |
-| `"Hello world"`                               | A string                                                                                                           |
-| `"${pkgs.bash}/bin/sh"`                       | A string containing an expression (expands to `"/nix/store/hash-bash-version/bin/sh"`)                             |
-| `true`, `false`                               | Booleans                                                                                                           |
-| `123`                                         | An integer                                                                                                         |
-| `./foo.png`                                   | A path (relative to the containing Nix expression)                                                                 |
-| *Compound values*                             |                                                                                                                    |
-| `{ x = 1; y = 2; }`                           | A set with attributes named `x` and `y`                                                                            |
-| `{ foo.bar = 1; }`                            | A nested set, equivalent to `{ foo = { bar = 1; }; }`                                                              |
-| `rec { x = "foo"; y = x + "bar"; }`           | A recursive set, equivalent to `{ x = "foo"; y = "foobar"; }`                                                      |
-| `[ "foo" "bar" ]`                             | A list with two elements                                                                                           |
-| *Operators*                                   |                                                                                                                    |
-| `"foo" + "bar"`                               | String concatenation                                                                                               |
-| `1 + 2`                                       | Integer addition                                                                                                   |
-| `"foo" == "f" + "oo"`                         | Equality test (evaluates to `true`)                                                                                |
-| `"foo" != "bar"`                              | Inequality test (evaluates to `true`)                                                                              |
-| `!true`                                       | Boolean negation                                                                                                   |
-| `{ x = 1; y = 2; }.x`                         | Attribute selection (evaluates to `1`)                                                                             |
-| `{ x = 1; y = 2; }.z or 3`                    | Attribute selection with default (evaluates to `3`)                                                                |
-| `{ x = 1; y = 2; } // { z = 3; }`             | Merge two sets (attributes in the right-hand set taking precedence)                                                |
-| *Control structures*                          |                                                                                                                    |
-| `if 1 + 1 == 2 then "yes!" else "no!"`        | Conditional expression                                                                                             |
-| `assert 1 + 1 == 2; "yes!"`                   | Assertion check (evaluates to `"yes!"`). See [](#sec-assertions) for using assertions in modules                   |
-| `let x = "foo"; y = "bar"; in x + y`          | Variable definition                                                                                                |
-| `with pkgs.lib; head [ 1 2 3 ]`               | Add all attributes from the given set to the scope (evaluates to `1`)                                              |
-| *Functions (lambdas)*                         |                                                                                                                    |
-| `x: x + 1`                                    | A function that expects an integer and returns it increased by 1                                                   |
-| `(x: x + 1) 100`                              | A function call (evaluates to 101)                                                                                 |
-| `let inc = x: x + 1; in inc (inc (inc 100))`  | A function bound to a variable and subsequently called by name (evaluates to 103)                                  |
-| `{ x, y }: x + y`                             | A function that expects a set with required attributes `x` and `y` and concatenates them                           |
-| `{ x, y ? "bar" }: x + y`                     | A function that expects a set with required attribute `x` and optional `y`, using `"bar"` as default value for `y` |
-| `{ x, y, ... }: x + y`                        | A function that expects a set with required attributes `x` and `y` and ignores any other attributes                |
-| `{ x, y } @ args: x + y`                      | A function that expects a set with required attributes `x` and `y`, and binds the whole set to `args`              |
-| *Built-in functions*                          |                                                                                                                    |
-| `import ./foo.nix`                            | Load and return Nix expression in given file                                                                       |
-| `map (x: x + x) [ 1 2 3 ]`                    | Apply a function to every element of a list (evaluates to `[ 2 4 6 ]`)                                             |
diff --git a/nixos/doc/manual/configuration/user-mgmt.chapter.md b/nixos/doc/manual/configuration/user-mgmt.chapter.md
index 37990664a8f1b..5c3aca3ef9e95 100644
--- a/nixos/doc/manual/configuration/user-mgmt.chapter.md
+++ b/nixos/doc/manual/configuration/user-mgmt.chapter.md
@@ -32,8 +32,7 @@ account will cease to exist. Also, imperative commands for managing users and
 groups, such as useradd, are no longer available. Passwords may still be
 assigned by setting the user\'s
 [hashedPassword](#opt-users.users._name_.hashedPassword) option. A
-hashed password can be generated using `mkpasswd -m
-  sha-512`.
+hashed password can be generated using `mkpasswd`.
 
 A user ID (uid) is assigned automatically. You can also specify a uid
 manually by adding
diff --git a/nixos/doc/manual/from_md/configuration/config-syntax.chapter.xml b/nixos/doc/manual/from_md/configuration/config-syntax.chapter.xml
index 01446e53e38ff..baf9639554cc2 100644
--- a/nixos/doc/manual/from_md/configuration/config-syntax.chapter.xml
+++ b/nixos/doc/manual/from_md/configuration/config-syntax.chapter.xml
@@ -17,5 +17,4 @@
   <xi:include href="config-file.section.xml" />
   <xi:include href="abstractions.section.xml" />
   <xi:include href="modularity.section.xml" />
-  <xi:include href="summary.section.xml" />
 </chapter>
diff --git a/nixos/doc/manual/from_md/configuration/summary.section.xml b/nixos/doc/manual/from_md/configuration/summary.section.xml
deleted file mode 100644
index 96a178c4930ed..0000000000000
--- a/nixos/doc/manual/from_md/configuration/summary.section.xml
+++ /dev/null
@@ -1,332 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-nix-syntax-summary">
-  <title>Syntax Summary</title>
-  <para>
-    Below is a summary of the most important syntactic constructs in the
-    Nix expression language. It’s not complete. In particular, there are
-    many other built-in functions. See the
-    <link xlink:href="https://nixos.org/nix/manual/#chap-writing-nix-expressions">Nix
-    manual</link> for the rest.
-  </para>
-  <informaltable>
-    <tgroup cols="2">
-      <colspec align="left" />
-      <colspec align="left" />
-      <thead>
-        <row>
-          <entry>
-            Example
-          </entry>
-          <entry>
-            Description
-          </entry>
-        </row>
-      </thead>
-      <tbody>
-        <row>
-          <entry>
-            <emphasis>Basic values</emphasis>
-          </entry>
-          <entry>
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>&quot;Hello world&quot;</literal>
-          </entry>
-          <entry>
-            A string
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>&quot;${pkgs.bash}/bin/sh&quot;</literal>
-          </entry>
-          <entry>
-            A string containing an expression (expands to
-            <literal>&quot;/nix/store/hash-bash-version/bin/sh&quot;</literal>)
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>true</literal>, <literal>false</literal>
-          </entry>
-          <entry>
-            Booleans
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>123</literal>
-          </entry>
-          <entry>
-            An integer
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>./foo.png</literal>
-          </entry>
-          <entry>
-            A path (relative to the containing Nix expression)
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <emphasis>Compound values</emphasis>
-          </entry>
-          <entry>
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>{ x = 1; y = 2; }</literal>
-          </entry>
-          <entry>
-            A set with attributes named <literal>x</literal> and
-            <literal>y</literal>
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>{ foo.bar = 1; }</literal>
-          </entry>
-          <entry>
-            A nested set, equivalent to
-            <literal>{ foo = { bar = 1; }; }</literal>
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>rec { x = &quot;foo&quot;; y = x + &quot;bar&quot;; }</literal>
-          </entry>
-          <entry>
-            A recursive set, equivalent to
-            <literal>{ x = &quot;foo&quot;; y = &quot;foobar&quot;; }</literal>
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>[ &quot;foo&quot; &quot;bar&quot; ]</literal>
-          </entry>
-          <entry>
-            A list with two elements
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <emphasis>Operators</emphasis>
-          </entry>
-          <entry>
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>&quot;foo&quot; + &quot;bar&quot;</literal>
-          </entry>
-          <entry>
-            String concatenation
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>1 + 2</literal>
-          </entry>
-          <entry>
-            Integer addition
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>&quot;foo&quot; == &quot;f&quot; + &quot;oo&quot;</literal>
-          </entry>
-          <entry>
-            Equality test (evaluates to <literal>true</literal>)
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>&quot;foo&quot; != &quot;bar&quot;</literal>
-          </entry>
-          <entry>
-            Inequality test (evaluates to <literal>true</literal>)
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>!true</literal>
-          </entry>
-          <entry>
-            Boolean negation
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>{ x = 1; y = 2; }.x</literal>
-          </entry>
-          <entry>
-            Attribute selection (evaluates to <literal>1</literal>)
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>{ x = 1; y = 2; }.z or 3</literal>
-          </entry>
-          <entry>
-            Attribute selection with default (evaluates to
-            <literal>3</literal>)
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>{ x = 1; y = 2; } // { z = 3; }</literal>
-          </entry>
-          <entry>
-            Merge two sets (attributes in the right-hand set taking
-            precedence)
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <emphasis>Control structures</emphasis>
-          </entry>
-          <entry>
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>if 1 + 1 == 2 then &quot;yes!&quot; else &quot;no!&quot;</literal>
-          </entry>
-          <entry>
-            Conditional expression
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>assert 1 + 1 == 2; &quot;yes!&quot;</literal>
-          </entry>
-          <entry>
-            Assertion check (evaluates to
-            <literal>&quot;yes!&quot;</literal>). See
-            <xref linkend="sec-assertions" /> for using assertions in
-            modules
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>let x = &quot;foo&quot;; y = &quot;bar&quot;; in x + y</literal>
-          </entry>
-          <entry>
-            Variable definition
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>with pkgs.lib; head [ 1 2 3 ]</literal>
-          </entry>
-          <entry>
-            Add all attributes from the given set to the scope
-            (evaluates to <literal>1</literal>)
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <emphasis>Functions (lambdas)</emphasis>
-          </entry>
-          <entry>
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>x: x + 1</literal>
-          </entry>
-          <entry>
-            A function that expects an integer and returns it increased
-            by 1
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>(x: x + 1) 100</literal>
-          </entry>
-          <entry>
-            A function call (evaluates to 101)
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>let inc = x: x + 1; in inc (inc (inc 100))</literal>
-          </entry>
-          <entry>
-            A function bound to a variable and subsequently called by
-            name (evaluates to 103)
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>{ x, y }: x + y</literal>
-          </entry>
-          <entry>
-            A function that expects a set with required attributes
-            <literal>x</literal> and <literal>y</literal> and
-            concatenates them
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>{ x, y ? &quot;bar&quot; }: x + y</literal>
-          </entry>
-          <entry>
-            A function that expects a set with required attribute
-            <literal>x</literal> and optional <literal>y</literal>,
-            using <literal>&quot;bar&quot;</literal> as default value
-            for <literal>y</literal>
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>{ x, y, ... }: x + y</literal>
-          </entry>
-          <entry>
-            A function that expects a set with required attributes
-            <literal>x</literal> and <literal>y</literal> and ignores
-            any other attributes
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>{ x, y } @ args: x + y</literal>
-          </entry>
-          <entry>
-            A function that expects a set with required attributes
-            <literal>x</literal> and <literal>y</literal>, and binds the
-            whole set to <literal>args</literal>
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <emphasis>Built-in functions</emphasis>
-          </entry>
-          <entry>
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>import ./foo.nix</literal>
-          </entry>
-          <entry>
-            Load and return Nix expression in given file
-          </entry>
-        </row>
-        <row>
-          <entry>
-            <literal>map (x: x + x) [ 1 2 3 ]</literal>
-          </entry>
-          <entry>
-            Apply a function to every element of a list (evaluates to
-            <literal>[ 2 4 6 ]</literal>)
-          </entry>
-        </row>
-      </tbody>
-    </tgroup>
-  </informaltable>
-</section>
diff --git a/nixos/doc/manual/from_md/configuration/user-mgmt.chapter.xml b/nixos/doc/manual/from_md/configuration/user-mgmt.chapter.xml
index 06492d5c25126..a2d7d2a9f1154 100644
--- a/nixos/doc/manual/from_md/configuration/user-mgmt.chapter.xml
+++ b/nixos/doc/manual/from_md/configuration/user-mgmt.chapter.xml
@@ -39,7 +39,7 @@ users.users.alice = {
     Passwords may still be assigned by setting the user's
     <link linkend="opt-users.users._name_.hashedPassword">hashedPassword</link>
     option. A hashed password can be generated using
-    <literal>mkpasswd -m sha-512</literal>.
+    <literal>mkpasswd</literal>.
   </para>
   <para>
     A user ID (uid) is assigned automatically. You can also specify a
diff --git a/nixos/doc/manual/from_md/installation/installing.chapter.xml b/nixos/doc/manual/from_md/installation/installing.chapter.xml
index 02a0f14b984ac..f2ed58c0c1fe5 100644
--- a/nixos/doc/manual/from_md/installation/installing.chapter.xml
+++ b/nixos/doc/manual/from_md/installation/installing.chapter.xml
@@ -455,8 +455,8 @@ OK
           <listitem>
             <para>
               Finally, add a <emphasis>swap</emphasis> partition. The
-              size required will vary according to needs, here a 8GiB
-              one is created.
+              size required will vary according to needs, here a 8GB one
+              is created.
             </para>
             <programlisting>
 # parted /dev/sda -- mkpart primary linux-swap -8GB 100%
@@ -814,8 +814,8 @@ $ passwd eelco
       </para>
       <programlisting>
 # parted /dev/sda -- mklabel msdos
-# parted /dev/sda -- mkpart primary 1MiB -8GiB
-# parted /dev/sda -- mkpart primary linux-swap -8GiB 100%
+# parted /dev/sda -- mkpart primary 1MB -8GB
+# parted /dev/sda -- mkpart primary linux-swap -8GB 100%
 </programlisting>
       <anchor xml:id="ex-partition-scheme-UEFI" />
       <para>
@@ -824,9 +824,9 @@ $ passwd eelco
       </para>
       <programlisting>
 # parted /dev/sda -- mklabel gpt
-# parted /dev/sda -- mkpart primary 512MiB -8GiB
-# parted /dev/sda -- mkpart primary linux-swap -8GiB 100%
-# parted /dev/sda -- mkpart ESP fat32 1MiB 512MiB
+# parted /dev/sda -- mkpart primary 512MB -8GB
+# parted /dev/sda -- mkpart primary linux-swap -8GB 100%
+# parted /dev/sda -- mkpart ESP fat32 1MB 512MB
 # parted /dev/sda -- set 3 esp on
 </programlisting>
       <anchor xml:id="ex-install-sequence" />
diff --git a/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml
index 20f8933da38f3..8e97e58f81a61 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml
@@ -141,6 +141,11 @@
       </listitem>
       <listitem>
         <para>
+          PHP 8.2.0 RC 6 is available.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           <literal>protonup</literal> has been aliased to and replaced
           by <literal>protonup-ng</literal> due to upstream not
           maintaining it.
@@ -626,6 +631,23 @@
       </listitem>
       <listitem>
         <para>
+          The <literal>openssl</literal>-extension for the PHP
+          interpreter used by Nextcloud is built against OpenSSL 1.1 if
+          <xref linkend="opt-system.stateVersion" /> is below
+          <literal>22.11</literal>. This is to make sure that people
+          using
+          <link xlink:href="https://docs.nextcloud.com/server/latest/admin_manual/configuration_files/encryption_configuration.html">server-side
+          encryption</link> don’t loose access to their files.
+        </para>
+        <para>
+          In any other case it’s safe to use OpenSSL 3 for PHP’s openssl
+          extension. This can be done by setting
+          <xref linkend="opt-services.nextcloud.enableBrokenCiphersForSSE" />
+          to <literal>false</literal>.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           The <literal>coq</literal> package and versioned variants
           starting at <literal>coq_8_14</literal> no longer include
           CoqIDE, which is now available through
@@ -1051,6 +1073,17 @@ services.github-runner.serviceOverrides.SupplementaryGroups = [
     <itemizedlist>
       <listitem>
         <para>
+          <literal>firefox</literal>, <literal>thunderbird</literal> and
+          <literal>librewolf</literal> come with enabled Wayland support
+          by default. The <literal>firefox-wayland</literal>,
+          <literal>firefox-esr-wayland</literal>,
+          <literal>thunderbird-wayland</literal> and
+          <literal>librewolf-wayland</literal> attributes are obsolete
+          and have been aliased to their generic attribute.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           The <literal>xplr</literal> package has been updated from
           0.18.0 to 0.19.0, which brings some breaking changes. See the
           <link xlink:href="https://github.com/sayanarijit/xplr/releases/tag/v0.19.0">upstream
@@ -1173,22 +1206,146 @@ services.github-runner.serviceOverrides.SupplementaryGroups = [
       </listitem>
       <listitem>
         <para>
-          The <literal>services.grafana</literal> options were converted
-          to a
+          The module <literal>services.grafana</literal> was refactored
+          to be compliant with
           <link xlink:href="https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md">RFC
-          0042</link> configuration.
-        </para>
-      </listitem>
-      <listitem>
-        <para>
-          The <literal>services.grafana.provision.datasources</literal>
-          and <literal>services.grafana.provision.dashboards</literal>
-          options were converted to a
-          <link xlink:href="https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md">RFC
-          0042</link> configuration. They also now support specifying
-          the provisioning YAML file with <literal>path</literal>
-          option.
+          0042</link>. To be precise, this means that the following
+          things have changed:
         </para>
+        <itemizedlist>
+          <listitem>
+            <para>
+              The newly introduced option
+              <xref linkend="opt-services.grafana.settings" /> is an
+              attribute-set that will be converted into Grafana’s INI
+              format. This means that the configuration from
+              <link xlink:href="https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/">Grafana’s
+              configuration reference</link> can be directly written as
+              attribute-set in Nix within this option.
+            </para>
+          </listitem>
+          <listitem>
+            <para>
+              The option
+              <literal>services.grafana.extraOptions</literal> has been
+              removed. This option was an association of environment
+              variables for Grafana. If you had an expression like
+            </para>
+            <programlisting language="bash">
+{
+  services.grafana.extraOptions.SECURITY_ADMIN_USER = &quot;foobar&quot;;
+}
+</programlisting>
+            <para>
+              your Grafana instance was running with
+              <literal>GF_SECURITY_ADMIN_USER=foobar</literal> in its
+              environment.
+            </para>
+            <para>
+              For the migration, it is recommended to turn it into the
+              INI format, i.e. to declare
+            </para>
+            <programlisting language="bash">
+{
+  services.grafana.settings.security.admin_user = &quot;foobar&quot;;
+}
+</programlisting>
+            <para>
+              instead.
+            </para>
+            <para>
+              The keys in
+              <literal>services.grafana.extraOptions</literal> have the
+              format
+              <literal>&lt;INI section name&gt;_&lt;Key Name&gt;</literal>.
+              Further details are outlined in the
+              <link xlink:href="https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#override-configuration-with-environment-variables">configuration
+              reference</link>.
+            </para>
+            <para>
+              Alternatively you can also set all your values from
+              <literal>extraOptions</literal> to
+              <literal>systemd.services.grafana.environment</literal>,
+              make sure you don’t forget to add the
+              <literal>GF_</literal> prefix though!
+            </para>
+          </listitem>
+          <listitem>
+            <para>
+              Previously, the options
+              <xref linkend="opt-services.grafana.provision.datasources" />
+              and
+              <xref linkend="opt-services.grafana.provision.dashboards" />
+              expected lists of datasources or dashboards for the
+              <link xlink:href="https://grafana.com/docs/grafana/latest/administration/provisioning/">declarative
+              provisioning</link>.
+            </para>
+            <para>
+              To declare lists of
+            </para>
+            <itemizedlist spacing="compact">
+              <listitem>
+                <para>
+                  <emphasis role="strong">datasources</emphasis>, please
+                  rename your declarations to
+                  <xref linkend="opt-services.grafana.provision.datasources.settings.datasources" />.
+                </para>
+              </listitem>
+              <listitem>
+                <para>
+                  <emphasis role="strong">dashboards</emphasis>, please
+                  rename your declarations to
+                  <xref linkend="opt-services.grafana.provision.dashboards.settings.providers" />.
+                </para>
+              </listitem>
+            </itemizedlist>
+            <para>
+              This change was made to support more features for that:
+            </para>
+            <itemizedlist>
+              <listitem>
+                <para>
+                  It’s possible to declare the
+                  <literal>apiVersion</literal> of your dashboards and
+                  datasources by
+                  <xref linkend="opt-services.grafana.provision.datasources.settings.apiVersion" />
+                  (or
+                  <xref linkend="opt-services.grafana.provision.dashboards.settings.apiVersion" />).
+                </para>
+              </listitem>
+              <listitem>
+                <para>
+                  Instead of declaring datasources and dashboards in
+                  pure Nix, it’s also possible to specify configuration
+                  files (or directories) with YAML instead using
+                  <xref linkend="opt-services.grafana.provision.datasources.path" />
+                  (or
+                  <xref linkend="opt-services.grafana.provision.dashboards.path" />.
+                  This is useful when having provisioning files from
+                  non-NixOS Grafana instances that you also want to
+                  deploy to NixOS.
+                </para>
+                <para>
+                  <emphasis role="strong">Note:</emphasis> secrets from
+                  these files will be leaked into the store unless you
+                  use a
+                  <link xlink:href="https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#file-provider"><emphasis role="strong">file</emphasis>-provider
+                  or env-var</link> for secrets!
+                </para>
+              </listitem>
+              <listitem>
+                <para>
+                  <xref linkend="opt-services.grafana.provision.notifiers" />
+                  is not affected by this change because this feature is
+                  deprecated by Grafana and will probably removed in
+                  Grafana 10. It’s recommended to use
+                  <literal>services.grafana.provision.alerting.contactPoints</literal>
+                  instead.
+                </para>
+              </listitem>
+            </itemizedlist>
+          </listitem>
+        </itemizedlist>
       </listitem>
       <listitem>
         <para>
@@ -1428,6 +1585,16 @@ services.github-runner.serviceOverrides.SupplementaryGroups = [
       </listitem>
       <listitem>
         <para>
+          The Mastodon package got upgraded from the major version 3 to
+          4. See the
+          <link xlink:href="https://github.com/mastodon/mastodon/releases/tag/v4.0.0">v4.0.0
+          release notes</link> for a list of changes. On standard
+          setups, no manual migration steps are required. Nevertheless,
+          a database backup is recommended.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           The <literal>nomad</literal> package now defaults to 1.3,
           which no longer has a downgrade path to releases 1.2 or older.
         </para>
diff --git a/nixos/doc/manual/installation/installing.chapter.md b/nixos/doc/manual/installation/installing.chapter.md
index 44e7e29421d65..2c86cb923a5c1 100644
--- a/nixos/doc/manual/installation/installing.chapter.md
+++ b/nixos/doc/manual/installation/installing.chapter.md
@@ -307,7 +307,7 @@ update /etc/fstab.
     ```
 
 4.  Finally, add a *swap* partition. The size required will vary
-    according to needs, here a 8GiB one is created.
+    according to needs, here a 8GB one is created.
 
     ```ShellSession
     # parted /dev/sda -- mkpart primary linux-swap -8GB 100%
@@ -543,8 +543,8 @@ corresponding configuration Nix expression.
 :::
 ```ShellSession
 # parted /dev/sda -- mklabel msdos
-# parted /dev/sda -- mkpart primary 1MiB -8GiB
-# parted /dev/sda -- mkpart primary linux-swap -8GiB 100%
+# parted /dev/sda -- mkpart primary 1MB -8GB
+# parted /dev/sda -- mkpart primary linux-swap -8GB 100%
 ```
 :::
 
@@ -554,9 +554,9 @@ corresponding configuration Nix expression.
 :::
 ```ShellSession
 # parted /dev/sda -- mklabel gpt
-# parted /dev/sda -- mkpart primary 512MiB -8GiB
-# parted /dev/sda -- mkpart primary linux-swap -8GiB 100%
-# parted /dev/sda -- mkpart ESP fat32 1MiB 512MiB
+# parted /dev/sda -- mkpart primary 512MB -8GB
+# parted /dev/sda -- mkpart primary linux-swap -8GB 100%
+# parted /dev/sda -- mkpart ESP fat32 1MB 512MB
 # parted /dev/sda -- set 3 esp on
 ```
 :::
diff --git a/nixos/doc/manual/release-notes/rl-2211.section.md b/nixos/doc/manual/release-notes/rl-2211.section.md
index d21acb57fa1dc..ecfba7215c8b2 100644
--- a/nixos/doc/manual/release-notes/rl-2211.section.md
+++ b/nixos/doc/manual/release-notes/rl-2211.section.md
@@ -57,6 +57,8 @@ In addition to numerous new and upgraded packages, this release has the followin
   `mod_php` usage we still enable `ZTS` (Zend Thread Safe). This has been a
   common practice for a long time in other distributions.
 
+- PHP 8.2.0 RC 6 is available.
+
 - `protonup` has been aliased to and replaced by `protonup-ng` due to upstream not maintaining it.
 
 - Perl has been updated to 5.36, and its core module `HTTP::Tiny` was patched to verify SSL/TLS certificates by default.
@@ -202,6 +204,13 @@ Available as [services.patroni](options.html#opt-services.patroni.enable).
 
 - The `p4` package now only includes the open-source Perforce Helix Core command-line client and APIs. It no longer installs the unfree Helix Core Server binaries `p4d`, `p4broker`, and `p4p`. To install the Helix Core Server binaries, use the `p4d` package instead.
 
+- The `openssl`-extension for the PHP interpreter used by Nextcloud is built against OpenSSL 1.1 if
+  [](#opt-system.stateVersion) is below `22.11`. This is to make sure that people using [server-side encryption](https://docs.nextcloud.com/server/latest/admin_manual/configuration_files/encryption_configuration.html)
+  don't loose access to their files.
+
+  In any other case it's safe to use OpenSSL 3 for PHP's openssl extension. This can be done by setting
+  [](#opt-services.nextcloud.enableBrokenCiphersForSSE) to `false`.
+
 - The `coq` package and versioned variants starting at `coq_8_14` no
   longer include CoqIDE, which is now available through
   `coqPackages.coqide`. It is still possible to get CoqIDE as part of
@@ -332,6 +341,8 @@ Available as [services.patroni](options.html#opt-services.patroni.enable).
 
 ## Other Notable Changes {#sec-release-22.11-notable-changes}
 
+- `firefox`, `thunderbird` and `librewolf` come with enabled Wayland support by default. The `firefox-wayland`, `firefox-esr-wayland`, `thunderbird-wayland` and `librewolf-wayland` attributes are obsolete and have been aliased to their generic attribute.
+
 - The `xplr` package has been updated from 0.18.0 to 0.19.0, which brings some breaking changes. See the [upstream release notes](https://github.com/sayanarijit/xplr/releases/tag/v0.19.0) for more details.
 
 - Configuring multiple GitHub runners is now possible through `services.github-runners.<name>`. The option `services.github-runner` remains.
@@ -368,9 +379,66 @@ Available as [services.patroni](options.html#opt-services.patroni.enable).
 
 - The `services.matrix-synapse` systemd unit has been hardened.
 
-- The `services.grafana` options were converted to a [RFC 0042](https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md) configuration.
+- The module `services.grafana` was refactored to be compliant with [RFC 0042](https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md). To be precise, this means that the following things have changed:
+  - The newly introduced option [](#opt-services.grafana.settings) is an attribute-set that
+    will be converted into Grafana's INI format. This means that the configuration from
+    [Grafana's configuration reference](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/)
+    can be directly written as attribute-set in Nix within this option.
+  - The option `services.grafana.extraOptions` has been removed. This option was an association
+    of environment variables for Grafana. If you had an expression like
+
+    ```nix
+    {
+      services.grafana.extraOptions.SECURITY_ADMIN_USER = "foobar";
+    }
+    ```
+
+    your Grafana instance was running with `GF_SECURITY_ADMIN_USER=foobar` in its environment.
+
+    For the migration, it is recommended to turn it into the INI format, i.e.
+    to declare
+
+    ```nix
+    {
+      services.grafana.settings.security.admin_user = "foobar";
+    }
+    ```
+
+    instead.
 
-- The `services.grafana.provision.datasources` and `services.grafana.provision.dashboards` options were converted to a [RFC 0042](https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md) configuration. They also now support specifying the provisioning YAML file with `path` option.
+    The keys in `services.grafana.extraOptions` have the format `<INI section name>_<Key Name>`.
+    Further details are outlined in the [configuration reference](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#override-configuration-with-environment-variables).
+
+    Alternatively you can also set all your values from `extraOptions` to
+    `systemd.services.grafana.environment`, make sure you don't forget to add
+    the `GF_` prefix though!
+  - Previously, the options [](#opt-services.grafana.provision.datasources) and
+    [](#opt-services.grafana.provision.dashboards) expected lists of datasources
+    or dashboards for the [declarative provisioning](https://grafana.com/docs/grafana/latest/administration/provisioning/).
+
+    To declare lists of
+    - **datasources**, please rename your declarations to [](#opt-services.grafana.provision.datasources.settings.datasources).
+    - **dashboards**, please rename your declarations to [](#opt-services.grafana.provision.dashboards.settings.providers).
+
+    This change was made to support more features for that:
+
+    - It's possible to declare the `apiVersion` of your dashboards and datasources
+      by [](#opt-services.grafana.provision.datasources.settings.apiVersion) (or
+      [](#opt-services.grafana.provision.dashboards.settings.apiVersion)).
+
+    - Instead of declaring datasources and dashboards in pure Nix, it's also possible
+      to specify configuration files (or directories) with YAML instead using
+      [](#opt-services.grafana.provision.datasources.path) (or
+      [](#opt-services.grafana.provision.dashboards.path). This is useful when having
+      provisioning files from non-NixOS Grafana instances that you also want to
+      deploy to NixOS.
+
+      __Note:__ secrets from these files will be leaked into the store unless you use a
+      [**file**-provider or env-var](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#file-provider) for secrets!
+
+    - [](#opt-services.grafana.provision.notifiers) is not affected by this change because
+      this feature is deprecated by Grafana and will probably removed in Grafana 10.
+      It's recommended to use `services.grafana.provision.alerting.contactPoints` instead.
 
 - The `services.grafana.provision.alerting` option was added. It includes suboptions for every alerting-related objects (with the exception of `notifiers`), which means it's now possible to configure modern Grafana alerting declaratively.
 
@@ -439,6 +507,8 @@ Available as [services.patroni](options.html#opt-services.patroni.enable).
 
 - There is a new module for the `xfconf` program (the Xfce configuration storage system), which has a dbus service.
 
+- The Mastodon package got upgraded from the major version 3 to 4. See the [v4.0.0 release notes](https://github.com/mastodon/mastodon/releases/tag/v4.0.0) for a list of changes. On standard setups, no manual migration steps are required. Nevertheless, a database backup is recommended.
+
 - The `nomad` package now defaults to 1.3, which no longer has a downgrade path to releases 1.2 or older.
 
 - The `nodePackages` package set now defaults to the LTS release in the `nodejs` package again, instead of being pinned to `nodejs-14_x`. Several updates to node2nix have been made for compatibility with newer Node.js and npm versions and a new `postRebuild` hook has been added for packages to perform extra build steps before the npm install step prunes dev dependencies.
diff --git a/nixos/lib/test-driver/test_driver/machine.py b/nixos/lib/test-driver/test_driver/machine.py
index e45c83086fb52..ffbc7c18e42b6 100644
--- a/nixos/lib/test-driver/test_driver/machine.py
+++ b/nixos/lib/test-driver/test_driver/machine.py
@@ -684,10 +684,10 @@ class Machine:
         with self.nested("waiting for {} to appear on tty {}".format(regexp, tty)):
             retry(tty_matches)
 
-    def send_chars(self, chars: str) -> None:
+    def send_chars(self, chars: str, delay: Optional[float] = 0.01) -> None:
         with self.nested("sending keys ‘{}‘".format(chars)):
             for char in chars:
-                self.send_key(char)
+                self.send_key(char, delay)
 
     def wait_for_file(self, filename: str) -> None:
         """Waits until the file exists in machine's file system."""
@@ -860,10 +860,11 @@ class Machine:
                 if matches is not None:
                     return
 
-    def send_key(self, key: str) -> None:
+    def send_key(self, key: str, delay: Optional[float] = 0.01) -> None:
         key = CHAR_TO_KEY.get(key, key)
         self.send_monitor_command("sendkey {}".format(key))
-        time.sleep(0.01)
+        if delay is not None:
+            time.sleep(delay)
 
     def send_console(self, chars: str) -> None:
         assert self.process
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index b538a0119c06d..2660b0e6c9388 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -35,7 +35,7 @@ let
   '';
 
   hashedPasswordDescription = ''
-    To generate a hashed password run `mkpasswd -m sha-512`.
+    To generate a hashed password run `mkpasswd`.
 
     If set to an empty string (`""`), this user will
     be able to log in without being asked for a password (but not via remote
@@ -592,6 +592,26 @@ in {
       '';
     };
 
+    # Warn about user accounts with deprecated password hashing schemes
+    system.activationScripts.hashes = {
+      deps = [ "users" ];
+      text = ''
+        users=()
+        while IFS=: read -r user hash tail; do
+          if [[ "$hash" = "$"* && ! "$hash" =~ ^\$(y|gy|7|2b|2y|2a|6)\$ ]]; then
+            users+=("$user")
+          fi
+        done </etc/shadow
+
+        if (( "''${#users[@]}" )); then
+          echo "
+        WARNING: The following user accounts rely on password hashes that will
+        be removed in NixOS 23.05. They should be renewed as soon as possible."
+          printf ' - %s\n' "''${users[@]}"
+        fi
+      '';
+    };
+
     # for backwards compatibility
     system.activationScripts.groups = stringAfter [ "users" ] "";
 
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares.nix
index 288cbc94a321b..3f3571d253825 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares.nix
@@ -14,8 +14,6 @@ in
     calamares-nixos
     calamares-nixos-autostart
     calamares-nixos-extensions
-    # Needed for calamares QML module packagechooserq
-    libsForQt5.full
     # Get list of locales
     glibcLocales
   ];
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index a886332e90b70..2a23a32eaba6e 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -382,6 +382,7 @@
   ./services/databases/pgmanage.nix
   ./services/databases/postgresql.nix
   ./services/databases/redis.nix
+  ./services/databases/surrealdb.nix
   ./services/databases/victoriametrics.nix
   ./services/desktops/accountsservice.nix
   ./services/desktops/bamf.nix
@@ -718,6 +719,7 @@
   ./services/monitoring/teamviewer.nix
   ./services/monitoring/telegraf.nix
   ./services/monitoring/thanos.nix
+  ./services/monitoring/tremor-rs.nix
   ./services/monitoring/tuptime.nix
   ./services/monitoring/unifi-poller.nix
   ./services/monitoring/ups.nix
diff --git a/nixos/modules/programs/steam.nix b/nixos/modules/programs/steam.nix
index adbbf5d9ed4b4..1b69aac98863b 100644
--- a/nixos/modules/programs/steam.nix
+++ b/nixos/modules/programs/steam.nix
@@ -4,17 +4,31 @@ with lib;
 
 let
   cfg = config.programs.steam;
-
-  steam = pkgs.steam.override {
-    extraLibraries = pkgs: with config.hardware.opengl;
-      if pkgs.hostPlatform.is64bit
-      then [ package ] ++ extraPackages
-      else [ package32 ] ++ extraPackages32;
-  };
 in {
   options.programs.steam = {
     enable = mkEnableOption (lib.mdDoc "steam");
 
+    package = mkOption {
+      type        = types.package;
+      default     = pkgs.steam.override {
+        extraLibraries = pkgs: with config.hardware.opengl;
+          if pkgs.hostPlatform.is64bit
+          then [ package ] ++ extraPackages
+          else [ package32 ] ++ extraPackages32;
+      };
+      defaultText = literalExpression ''
+        pkgs.steam.override {
+          extraLibraries = pkgs: with config.hardware.opengl;
+            if pkgs.hostPlatform.is64bit
+            then [ package ] ++ extraPackages
+            else [ package32 ] ++ extraPackages32;
+        }
+      '';
+      description = lib.mdDoc ''
+        steam package to use.
+      '';
+    };
+
     remotePlay.openFirewall = mkOption {
       type = types.bool;
       default = false;
@@ -44,7 +58,10 @@ in {
 
     hardware.steam-hardware.enable = true;
 
-    environment.systemPackages = [ steam steam.run ];
+    environment.systemPackages = [
+      cfg.package
+      cfg.package.run
+    ];
 
     networking.firewall = lib.mkMerge [
       (mkIf cfg.remotePlay.openFirewall {
diff --git a/nixos/modules/services/databases/surrealdb.nix b/nixos/modules/services/databases/surrealdb.nix
new file mode 100644
index 0000000000000..27269eb02f64e
--- /dev/null
+++ b/nixos/modules/services/databases/surrealdb.nix
@@ -0,0 +1,79 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+
+  cfg = config.services.surrealdb;
+in {
+
+  options = {
+    services.surrealdb = {
+      enable = mkEnableOption (lib.mdDoc "A scalable, distributed, collaborative, document-graph database, for the realtime web ");
+
+      dbPath = mkOption {
+        type = types.str;
+        description = lib.mdDoc ''
+          The path that surrealdb will write data to. Use null for in-memory.
+          Can be one of "memory", "file://:path", "tikv://:addr".
+        '';
+        default = "file:///var/lib/surrealdb/";
+        example = "memory";
+      };
+
+      host = mkOption {
+        type = types.str;
+        description = lib.mdDoc ''
+          The host that surrealdb will connect to.
+        '';
+        default = "127.0.0.1";
+        example = "127.0.0.1";
+      };
+
+      port = mkOption {
+        type = types.port;
+        description = lib.mdDoc ''
+          The port that surrealdb will connect to.
+        '';
+        default = 8000;
+        example = 8000;
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    # Used to connect to the running service
+    environment.systemPackages = [ pkgs.surrealdb ] ;
+
+    systemd.services.surrealdb = {
+      description = "A scalable, distributed, collaborative, document-graph database, for the realtime web ";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      serviceConfig = {
+        ExecStart = "${pkgs.surrealdb}/bin/surreal start --bind ${cfg.host}:${toString cfg.port} ${optionalString (cfg.dbPath != null) "-- ${cfg.dbPath}"}";
+        DynamicUser = true;
+        Restart = "on-failure";
+        StateDirectory = "surrealdb";
+        CapabilityBoundingSet = "";
+        NoNewPrivileges = true;
+        PrivateTmp = true;
+        ProtectHome = true;
+        ProtectClock = true;
+        ProtectProc = "noaccess";
+        ProcSubset = "pid";
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectControlGroups = true;
+        ProtectHostname = true;
+        RestrictSUIDSGID = true;
+        RestrictRealtime = true;
+        RestrictNamespaces = true;
+        LockPersonality = true;
+        RemoveIPC = true;
+        SystemCallFilter = [ "@system-service" "~@privileged" ];
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/desktops/gnome/at-spi2-core.nix b/nixos/modules/services/desktops/gnome/at-spi2-core.nix
index 495ea5af9879d..10a2f1f9eca0d 100644
--- a/nixos/modules/services/desktops/gnome/at-spi2-core.nix
+++ b/nixos/modules/services/desktops/gnome/at-spi2-core.nix
@@ -51,7 +51,10 @@ with lib;
     })
 
     (mkIf (!config.services.gnome.at-spi2-core.enable) {
-      environment.variables.NO_AT_BRIDGE = "1";
+      environment.variables = {
+        NO_AT_BRIDGE = "1";
+        GTK_A11Y = "none";
+      };
     })
   ];
 }
diff --git a/nixos/modules/services/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix
index 7a7f8330243a2..d95261332419d 100644
--- a/nixos/modules/services/hardware/udev.nix
+++ b/nixos/modules/services/hardware/udev.nix
@@ -46,6 +46,11 @@ let
     SUBSYSTEM=="input", KERNEL=="mice", TAG+="systemd"
   '';
 
+  nixosInitrdRules = ''
+    # Mark dm devices as db_persist so that they are kept active after switching root
+    SUBSYSTEM=="block", KERNEL=="dm-[0-9]*", ACTION=="add|change", OPTIONS+="db_persist"
+  '';
+
   # Perform substitutions in all udev rules files.
   udevRulesFor = { name, udevPackages, udevPath, udev, systemd, binPackages, initrdBin ? null }: pkgs.runCommand name
     { preferLocalBuild = true;
@@ -364,8 +369,10 @@ in
         EOF
       '';
 
+    boot.initrd.services.udev.rules = nixosInitrdRules;
+
     boot.initrd.systemd.additionalUpstreamUnits = [
-      # TODO: "initrd-udevadm-cleanup-db.service" is commented out because of https://github.com/systemd/systemd/issues/12953
+      "initrd-udevadm-cleanup-db.service"
       "systemd-udevd-control.socket"
       "systemd-udevd-kernel.socket"
       "systemd-udevd.service"
diff --git a/nixos/modules/services/matrix/mautrix-telegram.nix b/nixos/modules/services/matrix/mautrix-telegram.nix
index fc8b95051ddbe..2d9c2dc76c29b 100644
--- a/nixos/modules/services/matrix/mautrix-telegram.nix
+++ b/nixos/modules/services/matrix/mautrix-telegram.nix
@@ -7,8 +7,8 @@ let
   registrationFile = "${dataDir}/telegram-registration.yaml";
   cfg = config.services.mautrix-telegram;
   settingsFormat = pkgs.formats.json {};
-  settingsFileUnsubstituted = settingsFormat.generate "mautrix-telegram-config-unsubstituted.json" cfg.settings;
-  settingsFile = "${dataDir}/config.json";
+  settingsFile =
+    settingsFormat.generate "mautrix-telegram-config.json" cfg.settings;
 
 in {
   options = {
@@ -97,12 +97,23 @@ in {
         default = null;
         description = lib.mdDoc ''
           File containing environment variables to be passed to the mautrix-telegram service,
-          in which secret tokens can be specified securely by defining values for
+          in which secret tokens can be specified securely by defining values for e.g.
           `MAUTRIX_TELEGRAM_APPSERVICE_AS_TOKEN`,
           `MAUTRIX_TELEGRAM_APPSERVICE_HS_TOKEN`,
           `MAUTRIX_TELEGRAM_TELEGRAM_API_ID`,
           `MAUTRIX_TELEGRAM_TELEGRAM_API_HASH` and optionally
           `MAUTRIX_TELEGRAM_TELEGRAM_BOT_TOKEN`.
+
+          These environment variables can also be used to set other options by
+          replacing hierachy levels by `.`, converting the name to uppercase
+          and prepending `MAUTRIX_TELEGRAM_`.
+          For example, the first value above maps to
+          {option}`settings.appservice.as_token`.
+
+          The environment variable values can be prefixed with `json::` to have
+          them be parsed as JSON. For example, `login_shared_secret_map` can be
+          set as follows:
+          `MAUTRIX_TELEGRAM_BRIDGE_LOGIN_SHARED_SECRET_MAP=json::{"example.com":"secret"}`.
         '';
       };
 
@@ -141,16 +152,6 @@ in {
       environment.HOME = dataDir;
 
       preStart = ''
-        # Not all secrets can be passed as environment variable (yet)
-        # https://github.com/tulir/mautrix-telegram/issues/584
-        [ -f ${settingsFile} ] && rm -f ${settingsFile}
-        old_umask=$(umask)
-        umask 0177
-        ${pkgs.envsubst}/bin/envsubst \
-          -o ${settingsFile} \
-          -i ${settingsFileUnsubstituted}
-        umask $old_umask
-
         # generate the appservice's registration file if absent
         if [ ! -f '${registrationFile}' ]; then
           ${pkgs.mautrix-telegram}/bin/mautrix-telegram \
@@ -186,8 +187,6 @@ in {
             --config='${settingsFile}'
         '';
       };
-
-      restartTriggers = [ settingsFileUnsubstituted ];
     };
   };
 
diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix
index 52d5cab9f5159..9b3068796d8e3 100644
--- a/nixos/modules/services/monitoring/grafana.nix
+++ b/nixos/modules/services/monitoring/grafana.nix
@@ -13,57 +13,96 @@ let
   settingsFormatIni = pkgs.formats.ini {};
   configFile = settingsFormatIni.generate "config.ini" cfg.settings;
 
-  datasourceConfiguration = {
-    apiVersion = 1;
-    datasources = cfg.provision.datasources;
-  };
-
-  datasourceFileNew = if (cfg.provision.datasources.path == null) then provisioningSettingsFormat.generate "datasource.yaml" cfg.provision.datasources.settings else cfg.provision.datasources.path;
-  datasourceFile = if (builtins.isList cfg.provision.datasources) then provisioningSettingsFormat.generate "datasource.yaml" datasourceConfiguration else datasourceFileNew;
-
-  dashboardConfiguration = {
-    apiVersion = 1;
-    providers = cfg.provision.dashboards;
-  };
-
-  dashboardFileNew = if (cfg.provision.dashboards.path == null) then provisioningSettingsFormat.generate "dashboard.yaml" cfg.provision.dashboards.settings else cfg.provision.dashboards.path;
-  dashboardFile = if (builtins.isList cfg.provision.dashboards) then provisioningSettingsFormat.generate "dashboard.yaml" dashboardConfiguration else dashboardFileNew;
+  mkProvisionCfg = name: attr: provisionCfg:
+    if provisionCfg.path != null
+      then provisionCfg.path
+    else
+      provisioningSettingsFormat.generate "${name}.yaml"
+        (if provisionCfg.settings != null
+          then provisionCfg.settings
+          else {
+            apiVersion = 1;
+            ${attr} = [];
+          });
+
+  datasourceFileOrDir = mkProvisionCfg "datasource" "datasources" cfg.provision.datasources;
+  dashboardFileOrDir = mkProvisionCfg "dashboard" "providers" cfg.provision.dashboards;
 
   notifierConfiguration = {
     apiVersion = 1;
     notifiers = cfg.provision.notifiers;
   };
 
-  notifierFile = pkgs.writeText "notifier.yaml" (builtins.toJSON notifierConfiguration);
+  notifierFileOrDir = pkgs.writeText "notifier.yaml" (builtins.toJSON notifierConfiguration);
 
   generateAlertingProvisioningYaml = x: if (cfg.provision.alerting."${x}".path == null)
                                         then provisioningSettingsFormat.generate "${x}.yaml" cfg.provision.alerting."${x}".settings
                                         else cfg.provision.alerting."${x}".path;
-  rulesFile = generateAlertingProvisioningYaml "rules";
-  contactPointsFile = generateAlertingProvisioningYaml "contactPoints";
-  policiesFile = generateAlertingProvisioningYaml "policies";
-  templatesFile = generateAlertingProvisioningYaml "templates";
-  muteTimingsFile = generateAlertingProvisioningYaml "muteTimings";
-
-  provisionConfDir =  pkgs.runCommand "grafana-provisioning" { } ''
+  rulesFileOrDir = generateAlertingProvisioningYaml "rules";
+  contactPointsFileOrDir = generateAlertingProvisioningYaml "contactPoints";
+  policiesFileOrDir = generateAlertingProvisioningYaml "policies";
+  templatesFileOrDir = generateAlertingProvisioningYaml "templates";
+  muteTimingsFileOrDir = generateAlertingProvisioningYaml "muteTimings";
+
+  ln = { src, dir, filename }: ''
+    if [[ -d "${src}" ]]; then
+      pushd $out/${dir} &>/dev/null
+        lndir "${src}"
+      popd &>/dev/null
+    else
+      ln -sf ${src} $out/${dir}/${filename}.yaml
+    fi
+  '';
+  provisionConfDir = pkgs.runCommand "grafana-provisioning" { nativeBuildInputs = [ pkgs.xorg.lndir ]; } ''
     mkdir -p $out/{datasources,dashboards,notifiers,alerting}
-    ln -sf ${datasourceFile} $out/datasources/datasource.yaml
-    ln -sf ${dashboardFile} $out/dashboards/dashboard.yaml
-    ln -sf ${notifierFile} $out/notifiers/notifier.yaml
-    ln -sf ${rulesFile} $out/alerting/rules.yaml
-    ln -sf ${contactPointsFile} $out/alerting/contactPoints.yaml
-    ln -sf ${policiesFile} $out/alerting/policies.yaml
-    ln -sf ${templatesFile} $out/alerting/templates.yaml
-    ln -sf ${muteTimingsFile} $out/alerting/muteTimings.yaml
+    ${ln { src = datasourceFileOrDir;    dir = "datasources"; filename = "datasource"; }}
+    ${ln { src = dashboardFileOrDir;     dir = "dashboards";  filename = "dashbaord"; }}
+    ${ln { src = notifierFileOrDir;      dir = "notifiers";   filename = "notifier"; }}
+    ${ln { src = rulesFileOrDir;         dir = "alerting";    filename = "rules"; }}
+    ${ln { src = contactPointsFileOrDir; dir = "alerting";    filename = "contactPoints"; }}
+    ${ln { src = policiesFileOrDir;      dir = "alerting";    filename = "policies"; }}
+    ${ln { src = templatesFileOrDir;     dir = "alerting";    filename = "templates"; }}
+    ${ln { src = muteTimingsFileOrDir;   dir = "alerting";    filename = "muteTimings"; }}
   '';
 
   # Get a submodule without any embedded metadata:
   _filter = x: filterAttrs (k: v: k != "_module") x;
 
+  # FIXME(@Ma27) remove before 23.05. This is just a helper-type
+  # because `mkRenamedOptionModule` doesn't work if `foo.bar` is renamed
+  # to `foo.bar.baz`.
+  submodule' = module: types.coercedTo
+    (mkOptionType {
+      name = "grafana-provision-submodule";
+      description = "Wrapper-type for backwards compat of Grafana's declarative provisioning";
+      check = x:
+        if builtins.isList x then
+          throw ''
+            Provisioning dashboards and datasources declaratively by
+            setting `dashboards` or `datasources` to a list is not supported
+            anymore. Use `services.grafana.provision.datasources.settings.datasources`
+            (or `services.grafana.provision.dashboards.settings.providers`) instead.
+          ''
+        else isAttrs x || isFunction x;
+    })
+    id
+    (types.submodule module);
+
   # http://docs.grafana.org/administration/provisioning/#datasources
   grafanaTypes.datasourceConfig = types.submodule {
     freeformType = provisioningSettingsFormat.type;
 
+    imports = [
+      (mkRemovedOptionModule [ "password" ] ''
+        `services.grafana.provision.datasources.settings.datasources.<name>.password` has been removed
+        in Grafana 9. Use `secureJsonData` instead.
+      '')
+      (mkRemovedOptionModule [ "basicAuthPassword" ] ''
+        `services.grafana.provision.datasources.settings.datasources.<name>.basicAuthPassword` has been removed
+        in Grafana 9. Use `secureJsonData` instead.
+      '')
+    ];
+
     options = {
       name = mkOption {
         type = types.str;
@@ -93,28 +132,6 @@ let
         default = false;
         description = lib.mdDoc "Allow users to edit datasources from the UI.";
       };
-      password = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = lib.mdDoc ''
-          Database password, if used. Please note that the contents of this option
-          will end up in a world-readable Nix store. Use the file provider
-          pointing at a reasonably secured file in the local filesystem
-          to work around that. Look at the documentation for details:
-          <https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#file-provider>
-        '';
-      };
-      basicAuthPassword = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = lib.mdDoc ''
-          Basic auth password. Please note that the contents of this option
-          will end up in a world-readable Nix store. Use the file provider
-          pointing at a reasonably secured file in the local filesystem
-          to work around that. Look at the documentation for details:
-          <https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#file-provider>
-        '';
-      };
       secureJsonData = mkOption {
         type = types.nullOr types.attrs;
         default = null;
@@ -276,6 +293,10 @@ in {
     (mkRemovedOptionModule [ "services" "grafana" "auth" "google" "clientSecretFile" ] ''
       This option has been removed. Use 'services.grafana.settings.google.client_secret' with file provider instead.
     '')
+    (mkRemovedOptionModule [ "services" "grafana" "extraOptions" ] ''
+      This option has been removed. Use 'services.grafana.settings' instead. For a detailed migration guide, please
+      review the release notes of NixOS 22.11.
+    '')
 
     (mkRemovedOptionModule [ "services" "grafana" "auth" "azuread" "tenantId" ] "This option has been deprecated upstream.")
   ];
@@ -330,19 +351,7 @@ in {
                 Don't change the value of this option if you are planning to use `services.grafana.provision` options.
               '';
               default = provisionConfDir;
-              defaultText = literalExpression ''
-                pkgs.runCommand "grafana-provisioning" { } \'\'
-                  mkdir -p $out/{datasources,dashboards,notifiers,alerting}
-                  ln -sf ''${datasourceFile} $out/datasources/datasource.yaml
-                  ln -sf ''${dashboardFile} $out/dashboards/dashboard.yaml
-                  ln -sf ''${notifierFile} $out/notifiers/notifier.yaml
-                  ln -sf ''${rulesFile} $out/alerting/rules.yaml
-                  ln -sf ''${contactPointsFile} $out/alerting/contactPoints.yaml
-                  ln -sf ''${policiesFile} $out/alerting/policies.yaml
-                  ln -sf ''${templatesFile} $out/alerting/templates.yaml
-                  ln -sf ''${muteTimingsFile} $out/alerting/muteTimings.yaml
-                  \'\'
-              '';
+              defaultText = "directory with links to files generated from services.grafana.provision";
               type = types.path;
             };
           };
@@ -564,17 +573,14 @@ in {
 
       datasources = mkOption {
         description = lib.mdDoc ''
-          Deprecated option for Grafana datasource configuration. Use either
-          `services.grafana.provision.datasources.settings` or
-          `services.grafana.provision.datasources.path` instead.
+          Declaratively provision Grafana's datasources.
         '';
-        default = [];
-        apply = x: if (builtins.isList x) then map _filter x else x;
-        type = with types; either (listOf grafanaTypes.datasourceConfig) (submodule {
+        default = {};
+        type = submodule' {
           options.settings = mkOption {
             description = lib.mdDoc ''
               Grafana datasource configuration in Nix. Can't be used with
-              `services.grafana.provision.datasources.path` simultaneously. See
+              [](#opt-services.grafana.provision.datasources.path) simultaneously. See
               <https://grafana.com/docs/grafana/latest/administration/provisioning/#data-sources>
               for supported options.
             '';
@@ -591,6 +597,7 @@ in {
                   description = lib.mdDoc "List of datasources to insert/update.";
                   default = [];
                   type = types.listOf grafanaTypes.datasourceConfig;
+                  apply = map (flip builtins.removeAttrs [ "password" "basicAuthPassword" ]);
                 };
 
                 deleteDatasources = mkOption {
@@ -630,28 +637,26 @@ in {
           options.path = mkOption {
             description = lib.mdDoc ''
               Path to YAML datasource configuration. Can't be used with
-              `services.grafana.provision.datasources.settings` simultaneously.
+              [](#opt-services.grafana.provision.datasources.settings) simultaneously.
+              Can be either a directory or a single YAML file. Will end up in the store.
             '';
             default = null;
             type = types.nullOr types.path;
           };
-        });
+        };
       };
 
 
       dashboards = mkOption {
         description = lib.mdDoc ''
-          Deprecated option for Grafana dashboard configuration. Use either
-          `services.grafana.provision.dashboards.settings` or
-          `services.grafana.provision.dashboards.path` instead.
+          Declaratively provision Grafana's dashboards.
         '';
-        default = [];
-        apply = x: if (builtins.isList x) then map _filter x else x;
-        type = with types; either (listOf grafanaTypes.dashboardConfig) (submodule {
+        default = {};
+        type = submodule' {
           options.settings = mkOption {
             description = lib.mdDoc ''
               Grafana dashboard configuration in Nix. Can't be used with
-              `services.grafana.provision.dashboards.path` simultaneously. See
+              [](#opt-services.grafana.provision.dashboards.path) simultaneously. See
               <https://grafana.com/docs/grafana/latest/administration/provisioning/#dashboards>
               for supported options.
             '';
@@ -684,12 +689,13 @@ in {
           options.path = mkOption {
             description = lib.mdDoc ''
               Path to YAML dashboard configuration. Can't be used with
-              `services.grafana.provision.dashboards.settings` simultaneously.
+              [](#opt-services.grafana.provision.dashboards.settings) simultaneously.
+              Can be either a directory or a single YAML file. Will end up in the store.
             '';
             default = null;
             type = types.nullOr types.path;
           };
-        });
+        };
       };
 
 
@@ -706,7 +712,8 @@ in {
           path = mkOption {
             description = lib.mdDoc ''
               Path to YAML rules configuration. Can't be used with
-              `services.grafana.provision.alerting.rules.settings` simultaneously.
+              [](#opt-services.grafana.provision.alerting.rules.settings) simultaneously.
+              Can be either a directory or a single YAML file. Will end up in the store.
             '';
             default = null;
             type = types.nullOr types.path;
@@ -715,7 +722,7 @@ in {
           settings = mkOption {
             description = lib.mdDoc ''
               Grafana rules configuration in Nix. Can't be used with
-              `services.grafana.provision.alerting.rules.path` simultaneously. See
+              [](#opt-services.grafana.provision.alerting.rules.path) simultaneously. See
               <https://grafana.com/docs/grafana/latest/administration/provisioning/#rules>
               for supported options.
             '';
@@ -829,7 +836,8 @@ in {
           path = mkOption {
             description = lib.mdDoc ''
               Path to YAML contact points configuration. Can't be used with
-              `services.grafana.provision.alerting.contactPoints.settings` simultaneously.
+              [](#opt-services.grafana.provision.alerting.contactPoints.settings) simultaneously.
+              Can be either a directory or a single YAML file. Will end up in the store.
             '';
             default = null;
             type = types.nullOr types.path;
@@ -838,7 +846,7 @@ in {
           settings = mkOption {
             description = lib.mdDoc ''
               Grafana contact points configuration in Nix. Can't be used with
-              `services.grafana.provision.alerting.contactPoints.path` simultaneously. See
+              [](#opt-services.grafana.provision.alerting.contactPoints.path) simultaneously. See
               <https://grafana.com/docs/grafana/latest/administration/provisioning/#contact-points>
               for supported options.
             '';
@@ -852,7 +860,7 @@ in {
                 };
 
                 contactPoints = mkOption {
-                  description = lib.mdDoc "List of contact points to import or update. Please note that sensitive data will end up in world-readable Nix store.";
+                  description = lib.mdDoc "List of contact points to import or update.";
                   default = [];
                   type = types.listOf (types.submodule {
                     freeformType = provisioningSettingsFormat.type;
@@ -909,7 +917,8 @@ in {
           path = mkOption {
             description = lib.mdDoc ''
               Path to YAML notification policies configuration. Can't be used with
-              `services.grafana.provision.alerting.policies.settings` simultaneously.
+              [](#opt-services.grafana.provision.alerting.policies.settings) simultaneously.
+              Can be either a directory or a single YAML file. Will end up in the store.
             '';
             default = null;
             type = types.nullOr types.path;
@@ -918,7 +927,7 @@ in {
           settings = mkOption {
             description = lib.mdDoc ''
               Grafana notification policies configuration in Nix. Can't be used with
-              `services.grafana.provision.alerting.policies.path` simultaneously. See
+              [](#opt-services.grafana.provision.alerting.policies.path) simultaneously. See
               <https://grafana.com/docs/grafana/latest/administration/provisioning/#notification-policies>
               for supported options.
             '';
@@ -978,7 +987,8 @@ in {
           path = mkOption {
             description = lib.mdDoc ''
               Path to YAML templates configuration. Can't be used with
-              `services.grafana.provision.alerting.templates.settings` simultaneously.
+              [](#opt-services.grafana.provision.alerting.templates.settings) simultaneously.
+              Can be either a directory or a single YAML file. Will end up in the store.
             '';
             default = null;
             type = types.nullOr types.path;
@@ -987,7 +997,7 @@ in {
           settings = mkOption {
             description = lib.mdDoc ''
               Grafana templates configuration in Nix. Can't be used with
-              `services.grafana.provision.alerting.templates.path` simultaneously. See
+              [](#opt-services.grafana.provision.alerting.templates.path) simultaneously. See
               <https://grafana.com/docs/grafana/latest/administration/provisioning/#templates>
               for supported options.
             '';
@@ -1059,7 +1069,8 @@ in {
           path = mkOption {
             description = lib.mdDoc ''
               Path to YAML mute timings configuration. Can't be used with
-              `services.grafana.provision.alerting.muteTimings.settings` simultaneously.
+              [](#opt-services.grafana.provision.alerting.muteTimings.settings) simultaneously.
+              Can be either a directory or a single YAML file. Will end up in the store.
             '';
             default = null;
             type = types.nullOr types.path;
@@ -1068,7 +1079,7 @@ in {
           settings = mkOption {
             description = lib.mdDoc ''
               Grafana mute timings configuration in Nix. Can't be used with
-              `services.grafana.provision.alerting.muteTimings.path` simultaneously. See
+              [](#opt-services.grafana.provision.alerting.muteTimings.path) simultaneously. See
               <https://grafana.com/docs/grafana/latest/administration/provisioning/#mute-timings>
               for supported options.
             '';
@@ -1159,52 +1170,50 @@ in {
 
   config = mkIf cfg.enable {
     warnings = let
-      usesFileProvider = opt: defaultValue: builtins.match "^${defaultValue}$|^\\$__file\\{.*}$" opt != null;
-    in flatten [
-      (optional (
-        ! usesFileProvider cfg.settings.database.password "" ||
-        ! usesFileProvider cfg.settings.security.admin_password "admin"
-      ) "Grafana passwords will be stored as plaintext in the Nix store! Use file provider instead.")
-      (optional (
+      doesntUseFileProvider = opt: defaultValue:
         let
-          checkOpts = opt: any (x: x.password != null || x.basicAuthPassword != null || x.secureJsonData != null) opt;
-          datasourcesUsed = if (cfg.provision.datasources.settings == null) then [] else cfg.provision.datasources.settings.datasources;
-        in if (builtins.isList cfg.provision.datasources) then checkOpts cfg.provision.datasources else checkOpts datasourcesUsed
-        ) ''
-          Datasource passwords will be stored as plaintext in the Nix store!
-          It is not possible to use file provider in provisioning; please provision
-          datasources via `services.grafana.provision.datasources.path` instead.
-        '')
-      (optional (
-        any (x: x.secure_settings != null) cfg.provision.notifiers
-      ) "Notifier secure settings will be stored as plaintext in the Nix store! Use file provider instead.")
+          regex = "${optionalString (defaultValue != null) "^${defaultValue}$|"}^\\$__(file|env)\\{.*}$|^\\$[^_\\$][^ ]+$";
+        in builtins.match regex opt == null;
+    in
+      # Ensure that no custom credentials are leaked into the Nix store. Unless the default value
+      # is specified, this can be achieved by using the file/env provider:
+      # https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#variable-expansion
       (optional (
-        builtins.isList cfg.provision.datasources && cfg.provision.datasources != []
+        doesntUseFileProvider cfg.settings.database.password "" ||
+        doesntUseFileProvider cfg.settings.security.admin_password "admin"
       ) ''
-          Provisioning Grafana datasources with options has been deprecated.
-          Use `services.grafana.provision.datasources.settings` or
-          `services.grafana.provision.datasources.path` instead.
-        '')
-      (optional (
-        builtins.isList cfg.provision.datasources && cfg.provision.dashboards != []
+        Grafana passwords will be stored as plaintext in the Nix store!
+        Use file provider or an env-var instead.
+      '')
+      # Warn about deprecated notifiers.
+      ++ (optional (cfg.provision.notifiers != []) ''
+        Notifiers are deprecated upstream and will be removed in Grafana 10.
+        Use `services.grafana.provision.alerting.contactPoints` instead.
+      '')
+      # Ensure that `secureJsonData` of datasources provisioned via `datasources.settings`
+      # only uses file/env providers.
+      ++ (optional (
+        let
+          datasourcesToCheck = optionals
+            (cfg.provision.datasources.settings != null)
+            cfg.provision.datasources.settings.datasources;
+          declarationUnsafe = { secureJsonData, ... }:
+            secureJsonData != null
+            && any (flip doesntUseFileProvider null) (attrValues secureJsonData);
+        in any declarationUnsafe datasourcesToCheck
       ) ''
-          Provisioning Grafana dashboards with options has been deprecated.
-          Use `services.grafana.provision.dashboards.settings` or
-          `services.grafana.provision.dashboards.path` instead.
-        '')
-      (optional (
-        cfg.provision.notifiers != []
-        ) ''
-            Notifiers are deprecated upstream and will be removed in Grafana 10.
-            Use `services.grafana.provision.alerting.contactPoints` instead.
-        '')
-    ];
+        Declarations in the `secureJsonData`-block of a datasource will be leaked to the
+        Nix store unless a file-provider or an env-var is used!
+      '')
+      ++ (optional (
+        any (x: x.secure_settings != null) cfg.provision.notifiers
+      ) "Notifier secure settings will be stored as plaintext in the Nix store! Use file provider instead.");
 
     environment.systemPackages = [ cfg.package ];
 
     assertions = [
       {
-        assertion = if (builtins.isList cfg.provision.datasources) then true else cfg.provision.datasources.settings == null || cfg.provision.datasources.path == null;
+        assertion = cfg.provision.datasources.settings == null || cfg.provision.datasources.path == null;
         message = "Cannot set both datasources settings and datasources path";
       }
       {
@@ -1213,12 +1222,11 @@ in {
           ({ type, access, ... }: type == "prometheus" -> access != "direct")
           opt;
         in
-          if (builtins.isList cfg.provision.datasources) then prometheusIsNotDirect cfg.provision.datasources
-          else cfg.provision.datasources.settings == null || prometheusIsNotDirect cfg.provision.datasources.settings.datasources;
+          cfg.provision.datasources.settings == null || prometheusIsNotDirect cfg.provision.datasources.settings.datasources;
         message = "For datasources of type `prometheus`, the `direct` access mode is not supported anymore (since Grafana 9.2.0)";
       }
       {
-        assertion = if (builtins.isList cfg.provision.dashboards) then true else cfg.provision.dashboards.settings == null || cfg.provision.dashboards.path == null;
+        assertion = cfg.provision.dashboards.settings == null || cfg.provision.dashboards.path == null;
         message = "Cannot set both dashboards settings and dashboards path";
       }
       {
diff --git a/nixos/modules/services/monitoring/tremor-rs.nix b/nixos/modules/services/monitoring/tremor-rs.nix
new file mode 100644
index 0000000000000..213e8a474868c
--- /dev/null
+++ b/nixos/modules/services/monitoring/tremor-rs.nix
@@ -0,0 +1,129 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+
+  cfg = config.services.tremor-rs;
+
+  loggerSettingsFormat = pkgs.formats.yaml { };
+  loggerConfigFile = loggerSettingsFormat.generate "logger.yaml" cfg.loggerSettings;
+in {
+
+  options = {
+    services.tremor-rs = {
+      enable = lib.mkEnableOption (lib.mdDoc "Tremor event- or stream-processing system");
+
+      troyFileList = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        description = lib.mdDoc "List of troy files to load.";
+      };
+
+      tremorLibDir = mkOption {
+        type = types.path;
+        default = "";
+        description = lib.mdDoc "Directory where to find /lib containing tremor script files";
+      };
+
+      host = mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        description = lib.mdDoc "The host tremor should be listening on";
+      };
+
+      port = mkOption {
+        type = types.port;
+        default = 9898;
+        description = lib.mdDoc "the port tremor should be listening on";
+      };
+
+      loggerSettings = mkOption {
+        description = lib.mdDoc "Tremor logger configuration";
+        default = {};
+        type = loggerSettingsFormat.type;
+
+        example = {
+          refresh_rate = "30 seconds";
+          appenders.stdout.kind = "console";
+          root = {
+            level = "warn";
+            appenders = [ "stdout" ];
+          };
+          loggers = {
+            tremor_runtime = {
+              level = "debug";
+              appenders = [ "stdout" ];
+              additive = false;
+            };
+            tremor = {
+              level = "debug";
+              appenders = [ "stdout" ];
+              additive = false;
+            };
+          };
+        };
+
+        defaultText = literalExpression ''
+          {
+            refresh_rate = "30 seconds";
+            appenders.stdout.kind = "console";
+            root = {
+              level = "warn";
+              appenders = [ "stdout" ];
+            };
+            loggers = {
+              tremor_runtime = {
+                level = "debug";
+                appenders = [ "stdout" ];
+                additive = false;
+              };
+              tremor = {
+                level = "debug";
+                appenders = [ "stdout" ];
+                additive = false;
+              };
+            };
+          }
+        '';
+
+      };
+    };
+  };
+
+  config = mkIf (cfg.enable) {
+
+    environment.systemPackages = [ pkgs.tremor-rs ] ;
+
+    systemd.services.tremor-rs = {
+      description = "Tremor event- or stream-processing system";
+      wantedBy = [ "multi-user.target" ];
+      requires = [ "network-online.target" ];
+      after = [ "network-online.target" ];
+
+      environment.TREMOR_PATH = "${pkgs.tremor-rs}/lib:${cfg.tremorLibDir}";
+
+      serviceConfig = {
+        ExecStart = "${pkgs.tremor-rs}/bin/tremor --logger-config ${loggerConfigFile} server run ${concatStringsSep " " cfg.troyFileList} --api-host ${cfg.host}:${toString cfg.port}";
+        DynamicUser = true;
+        Restart = "always";
+        NoNewPrivileges = true;
+        PrivateTmp = true;
+        ProtectHome = true;
+        ProtectClock = true;
+        ProtectProc = "noaccess";
+        ProcSubset = "pid";
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectControlGroups = true;
+        ProtectHostname = true;
+        RestrictSUIDSGID = true;
+        RestrictRealtime = true;
+        RestrictNamespaces = true;
+        LockPersonality = true;
+        RemoveIPC = true;
+        SystemCallFilter = [ "@system-service" "~@privileged" ];
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix
index 0242a3780ffc5..27119dcc57c55 100644
--- a/nixos/modules/services/networking/firewall.nix
+++ b/nixos/modules/services/networking/firewall.nix
@@ -325,7 +325,7 @@ in
         example = literalExpression "pkgs.iptables-legacy";
         description =
           lib.mdDoc ''
-            The iptables package to use for running the firewall service."
+            The iptables package to use for running the firewall service.
           '';
       };
 
diff --git a/nixos/modules/services/networking/mullvad-vpn.nix b/nixos/modules/services/networking/mullvad-vpn.nix
index 7eb3761aad37b..82e68bf92af1f 100644
--- a/nixos/modules/services/networking/mullvad-vpn.nix
+++ b/nixos/modules/services/networking/mullvad-vpn.nix
@@ -14,6 +14,15 @@ with lib;
       '';
     };
 
+    enableExcludeWrapper = mkOption {
+      type = types.bool;
+      default = true;
+      description = lib.mdDoc ''
+        This option activates the wrapper that allows the use of mullvad-exclude.
+        Might have minor security impact, so consider disabling if you do not use the feature.
+      '';
+    };
+
     package = mkOption {
       type = types.package;
       default = pkgs.mullvad;
@@ -27,12 +36,22 @@ with lib;
   config = mkIf cfg.enable {
     boot.kernelModules = [ "tun" ];
 
+    environment.systemPackages = [ cfg.package ];
+
     # mullvad-daemon writes to /etc/iproute2/rt_tables
     networking.iproute2.enable = true;
 
     # See https://github.com/NixOS/nixpkgs/issues/113589
     networking.firewall.checkReversePath = "loose";
 
+    # See https://github.com/NixOS/nixpkgs/issues/176603
+    security.wrappers.mullvad-exclude = mkIf cfg.enableExcludeWrapper {
+      setuid = true;
+      owner = "root";
+      group = "root";
+      source = "${cfg.package}/bin/mullvad-exclude";
+    };
+
     systemd.services.mullvad-daemon = {
       description = "Mullvad VPN daemon";
       wantedBy = [ "multi-user.target" ];
diff --git a/nixos/modules/services/networking/tailscale.nix b/nixos/modules/services/networking/tailscale.nix
index eb3afe118c644..26997dd96013c 100644
--- a/nixos/modules/services/networking/tailscale.nix
+++ b/nixos/modules/services/networking/tailscale.nix
@@ -41,7 +41,11 @@ in {
   };
 
   config = mkIf cfg.enable {
-    warnings = optional (firewallOn && rpfIsStrict) "Strict reverse path filtering breaks Tailscale exit node use and some subnet routing setups. Consider setting `networking.firewall.checkReversePath` = 'loose'";
+    warnings = optional (firewallOn && rpfIsStrict) ''
+      Strict reverse path filtering breaks Tailscale exit node use and some subnet routing setups. Consider setting:
+
+        networking.firewall.checkReversePath = "loose";
+    '';
     environment.systemPackages = [ cfg.package ]; # for the CLI
     systemd.packages = [ cfg.package ];
     systemd.services.tailscaled = {
diff --git a/nixos/modules/services/system/dbus.nix b/nixos/modules/services/system/dbus.nix
index c0de00bb914ca..b87e48f2945d1 100644
--- a/nixos/modules/services/system/dbus.nix
+++ b/nixos/modules/services/system/dbus.nix
@@ -1,8 +1,6 @@
 # D-Bus configuration and system bus daemon.
 
-{ config, lib, options, pkgs, ... }:
-
-with lib;
+{ config, lib, pkgs, ... }:
 
 let
 
@@ -16,11 +14,11 @@ let
     serviceDirectories = cfg.packages;
   };
 
+  inherit (lib) mkOption types;
+
 in
 
 {
-  ###### interface
-
   options = {
 
     services.dbus = {
@@ -65,31 +63,13 @@ in
         '';
         default = "disabled";
       };
-
-      socketActivated = mkOption {
-        type = types.nullOr types.bool;
-        default = null;
-        visible = false;
-        description = lib.mdDoc ''
-          Removed option, do not use.
-        '';
-      };
     };
   };
 
-  ###### implementation
-
-  config = mkIf cfg.enable {
-    warnings = optional (cfg.socketActivated != null) (
-      let
-        files = showFiles options.services.dbus.socketActivated.files;
-      in
-        "The option 'services.dbus.socketActivated' in ${files} no longer has"
-        + " any effect and can be safely removed: the user D-Bus session is"
-        + " now always socket activated."
-    );
-
-    environment.systemPackages = [ pkgs.dbus.daemon pkgs.dbus ];
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = [
+      pkgs.dbus
+    ];
 
     environment.etc."dbus-1".source = configDir;
 
@@ -102,10 +82,12 @@ in
 
     users.groups.messagebus.gid = config.ids.gids.messagebus;
 
-    systemd.packages = [ pkgs.dbus.daemon ];
+    systemd.packages = [
+      pkgs.dbus
+    ];
 
     security.wrappers.dbus-daemon-launch-helper = {
-      source = "${pkgs.dbus.daemon}/libexec/dbus-daemon-launch-helper";
+      source = "${pkgs.dbus}/libexec/dbus-daemon-launch-helper";
       owner = "root";
       group = "messagebus";
       setuid = true;
@@ -114,26 +96,36 @@ in
     };
 
     services.dbus.packages = [
-      pkgs.dbus.out
+      pkgs.dbus
       config.system.path
     ];
 
     systemd.services.dbus = {
       # Don't restart dbus-daemon. Bad things tend to happen if we do.
       reloadIfChanged = true;
-      restartTriggers = [ configDir ];
-      environment = { LD_LIBRARY_PATH = config.system.nssModules.path; };
+      restartTriggers = [
+        configDir
+      ];
+      environment = {
+        LD_LIBRARY_PATH = config.system.nssModules.path;
+      };
     };
 
-    systemd.user = {
-      services.dbus = {
-        # Don't restart dbus-daemon. Bad things tend to happen if we do.
-        reloadIfChanged = true;
-        restartTriggers = [ configDir ];
-      };
-      sockets.dbus.wantedBy = [ "sockets.target" ];
+    systemd.user.services.dbus = {
+      # Don't restart dbus-daemon. Bad things tend to happen if we do.
+      reloadIfChanged = true;
+      restartTriggers = [
+        configDir
+      ];
     };
 
-    environment.pathsToLink = [ "/etc/dbus-1" "/share/dbus-1" ];
+    systemd.user.sockets.dbus.wantedBy = [
+      "sockets.target"
+    ];
+
+    environment.pathsToLink = [
+      "/etc/dbus-1"
+      "/share/dbus-1"
+    ];
   };
 }
diff --git a/nixos/modules/services/web-apps/alps.nix b/nixos/modules/services/web-apps/alps.nix
index b73cb82925d9b..4681739af4ab0 100644
--- a/nixos/modules/services/web-apps/alps.nix
+++ b/nixos/modules/services/web-apps/alps.nix
@@ -70,6 +70,23 @@ in {
         '';
       };
     };
+
+    package = mkOption {
+      internal = true;
+      type = types.package;
+      default = pkgs.alps;
+    };
+
+    args = mkOption {
+      internal = true;
+      type = types.listOf types.str;
+      default = [
+        "-addr" "${cfg.bindIP}:${toString cfg.port}"
+        "-theme" "${cfg.theme}"
+        "imaps://${cfg.imaps.host}:${toString cfg.imaps.port}"
+        "smpts://${cfg.smtps.host}:${toString cfg.smtps.port}"
+      ];
+    };
   };
 
   config = mkIf cfg.enable {
@@ -80,16 +97,33 @@ in {
       after = [ "network.target" "network-online.target" ];
 
       serviceConfig = {
-        ExecStart = ''
-          ${pkgs.alps}/bin/alps \
-            -addr ${cfg.bindIP}:${toString cfg.port} \
-            -theme ${cfg.theme} \
-            imaps://${cfg.imaps.host}:${toString cfg.imaps.port} \
-            smpts://${cfg.smtps.host}:${toString cfg.smtps.port}
-        '';
-        StateDirectory = "alps";
-        WorkingDirectory = "/var/lib/alps";
+        ExecStart = "${cfg.package}/bin/alps ${escapeShellArgs cfg.args}";
         DynamicUser = true;
+        ## This is desirable but would restrict bindIP to 127.0.0.1
+        #IPAddressAllow = "localhost";
+        #IPAddressDeny = "any";
+        LockPersonality = true;
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        PrivateIPC = true;
+        PrivateTmp = true;
+        PrivateUsers = true;
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        ProtectSystem = "strict";
+        RemoveIPC = true;
+        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [ "@system-service @resources" "~@privileged @obsolete" ];
       };
     };
   };
diff --git a/nixos/modules/services/web-apps/keycloak.nix b/nixos/modules/services/web-apps/keycloak.nix
index 521cf778a36bf..d52190a28648e 100644
--- a/nixos/modules/services/web-apps/keycloak.nix
+++ b/nixos/modules/services/web-apps/keycloak.nix
@@ -482,6 +482,10 @@ in
             assertion = (cfg.database.useSSL && cfg.database.type == "postgresql") -> (cfg.database.caCert != null);
             message = "A CA certificate must be specified (in 'services.keycloak.database.caCert') when PostgreSQL is used with SSL";
           }
+          {
+            assertion = createLocalPostgreSQL -> config.services.postgresql.settings.standard_conforming_strings or true;
+            message = "Setting up a local PostgreSQL db for Keycloak requires `standard_conforming_strings` turned on to work reliably";
+          }
         ];
 
         environment.systemPackages = [ keycloakBuild ];
@@ -544,7 +548,13 @@ in
             create_role="$(mktemp)"
             trap 'rm -f "$create_role"' EXIT
 
+            # Read the password from the credentials directory and
+            # escape any single quotes by adding additional single
+            # quotes after them, following the rules laid out here:
+            # https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-CONSTANTS
             db_password="$(<"$CREDENTIALS_DIRECTORY/db_password")"
+            db_password="''${db_password//\'/\'\'}"
+
             echo "CREATE ROLE keycloak WITH LOGIN PASSWORD '$db_password' CREATEDB" > "$create_role"
             psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='keycloak'" | grep -q 1 || psql -tA --file="$create_role"
             psql -tAc "SELECT 1 FROM pg_database WHERE datname = 'keycloak'" | grep -q 1 || psql -tAc 'CREATE DATABASE "keycloak" OWNER "keycloak"'
@@ -566,8 +576,16 @@ in
           script = ''
             set -o errexit -o pipefail -o nounset -o errtrace
             shopt -s inherit_errexit
+
+            # Read the password from the credentials directory and
+            # escape any single quotes by adding additional single
+            # quotes after them, following the rules laid out here:
+            # https://dev.mysql.com/doc/refman/8.0/en/string-literals.html
             db_password="$(<"$CREDENTIALS_DIRECTORY/db_password")"
-            ( echo "CREATE USER IF NOT EXISTS 'keycloak'@'localhost' IDENTIFIED BY '$db_password';"
+            db_password="''${db_password//\'/\'\'}"
+
+            ( echo "SET sql_mode = 'NO_BACKSLASH_ESCAPES';"
+              echo "CREATE USER IF NOT EXISTS 'keycloak'@'localhost' IDENTIFIED BY '$db_password';"
               echo "CREATE DATABASE IF NOT EXISTS keycloak CHARACTER SET utf8 COLLATE utf8_unicode_ci;"
               echo "GRANT ALL PRIVILEGES ON keycloak.* TO 'keycloak'@'localhost';"
             ) | mysql -N
@@ -632,12 +650,17 @@ in
 
               ${secretReplacements}
 
+              # Escape any backslashes in the db parameters, since
+              # they're otherwise unexpectedly read as escape
+              # sequences.
+              sed -i '/db-/ s|\\|\\\\|g' /run/keycloak/conf/keycloak.conf
+
             '' + optionalString (cfg.sslCertificate != null && cfg.sslCertificateKey != null) ''
               mkdir -p /run/keycloak/ssl
               cp $CREDENTIALS_DIRECTORY/ssl_{cert,key} /run/keycloak/ssl/
             '' + ''
               export KEYCLOAK_ADMIN=admin
-              export KEYCLOAK_ADMIN_PASSWORD=${cfg.initialAdminPassword}
+              export KEYCLOAK_ADMIN_PASSWORD=${escapeShellArg cfg.initialAdminPassword}
               kc.sh start --optimized
             '';
           };
diff --git a/nixos/modules/services/web-apps/mastodon.nix b/nixos/modules/services/web-apps/mastodon.nix
index d159d2ade0630..1e9e04dcc0558 100644
--- a/nixos/modules/services/web-apps/mastodon.nix
+++ b/nixos/modules/services/web-apps/mastodon.nix
@@ -688,7 +688,7 @@ in {
           inherit (cfg) group;
         };
       })
-      (lib.attrsets.setAttrByPath [ cfg.user "packages" ] [ cfg.package mastodonEnv ])
+      (lib.attrsets.setAttrByPath [ cfg.user "packages" ] [ cfg.package mastodonEnv pkgs.imagemagick ])
     ];
 
     users.groups.${cfg.group}.members = lib.optional cfg.configureNginx config.services.nginx.user;
diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix
index 04599884f139c..da621573f2a26 100644
--- a/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixos/modules/services/web-apps/nextcloud.nix
@@ -13,7 +13,12 @@ let
   phpPackage = cfg.phpPackage.buildEnv {
     extensions = { enabled, all }:
       (with all;
-        enabled
+        # disable default openssl extension
+        (lib.filter (e: e.pname != "php-openssl") enabled)
+        # use OpenSSL 1.1 for RC4 Nextcloud encryption if user
+        # has acknowledged the brokeness of the ciphers (RC4).
+        # TODO: remove when https://github.com/nextcloud/server/issues/32003 is fixed.
+        ++ (if cfg.enableBrokenCiphersForSSE then [ cfg.phpPackage.extensions.openssl-legacy ] else [ cfg.phpPackage.extensions.openssl ])
         ++ optional cfg.enableImagemagick imagick
         # Optionally enabled depending on caching settings
         ++ optional cfg.caching.apcu apcu
@@ -80,6 +85,40 @@ in {
 
   options.services.nextcloud = {
     enable = mkEnableOption (lib.mdDoc "nextcloud");
+
+    enableBrokenCiphersForSSE = mkOption {
+      type = types.bool;
+      default = versionOlder stateVersion "22.11";
+      defaultText = literalExpression "versionOlder system.stateVersion \"22.11\"";
+      description = lib.mdDoc ''
+        This option enables using the OpenSSL PHP extension linked against OpenSSL 1.1
+        rather than latest OpenSSL (≥ 3), this is not recommended unless you need
+        it for server-side encryption (SSE). SSE uses the legacy RC4 cipher which is
+        considered broken for several years now. See also [RFC7465](https://datatracker.ietf.org/doc/html/rfc7465).
+
+        This cipher has been disabled in OpenSSL ≥ 3 and requires
+        a specific legacy profile to re-enable it.
+
+        If you deploy Nextcloud using OpenSSL ≥ 3 for PHP and have
+        server-side encryption configured, you will not be able to access
+        your files anymore. Enabling this option can restore access to your files.
+        Upon testing we didn't encounter any data corruption when turning
+        this on and off again, but this cannot be guaranteed for
+        each Nextcloud installation.
+
+        It is `true` by default for systems with a [](#opt-system.stateVersion) below
+        `22.11` to make sure that existing installations won't break on update. On newer
+        NixOS systems you have to explicitly enable it on your own.
+
+        Please note that this only provides additional value when using
+        external storage such as S3 since it's not an end-to-end encryption.
+        If this is not the case,
+        it is advised to [disable server-side encryption](https://docs.nextcloud.com/server/latest/admin_manual/configuration_files/encryption_configuration.html#disabling-encryption) and set this to `false`.
+
+        In the future, Nextcloud may move to AES-256-GCM, by then,
+        this option will be removed.
+      '';
+    };
     hostName = mkOption {
       type = types.str;
       description = lib.mdDoc "FQDN for the nextcloud instance.";
@@ -649,6 +688,23 @@ in {
         ++ (optional (versionOlder cfg.package.version "23") (upgradeWarning 22 "22.05"))
         ++ (optional (versionOlder cfg.package.version "24") (upgradeWarning 23 "22.05"))
         ++ (optional (versionOlder cfg.package.version "25") (upgradeWarning 24 "22.11"))
+        ++ (optional cfg.enableBrokenCiphersForSSE ''
+          You're using PHP's openssl extension built against OpenSSL 1.1 for Nextcloud.
+          This is only necessary if you're using Nextcloud's server-side encryption.
+          Please keep in mind that it's using the broken RC4 cipher.
+
+          If you don't use that feature, you can switch to OpenSSL 3 and get
+          rid of this warning by declaring
+
+            services.nextcloud.enableBrokenCiphersForSSE = false;
+
+          If you need to use server-side encryption you can ignore this waring.
+          Otherwise you'd have to disable server-side encryption first in order
+          to be able to safely disable this option and get rid of this warning.
+          See <https://docs.nextcloud.com/server/latest/admin_manual/configuration_files/encryption_configuration.html#disabling-encryption> on how to achieve this.
+
+          For more context, here is the implementing pull request: https://github.com/NixOS/nixpkgs/pull/198470
+        '')
         ++ (optional isUnsupportedMariadb ''
             You seem to be using MariaDB at an unsupported version (i.e. at least 10.6)!
             Please note that this isn't supported officially by Nextcloud. You can either
diff --git a/nixos/modules/services/web-apps/nextcloud.xml b/nixos/modules/services/web-apps/nextcloud.xml
index a0b69dbd606ce..ca57692fc16a8 100644
--- a/nixos/modules/services/web-apps/nextcloud.xml
+++ b/nixos/modules/services/web-apps/nextcloud.xml
@@ -170,6 +170,20 @@
      </listitem>
     </itemizedlist>
    </listitem>
+   <listitem>
+    <formalpara>
+     <title>Server-side encryption</title>
+     <para>
+      Nextcloud supports <link xlink:href="https://docs.nextcloud.com/server/latest/admin_manual/configuration_files/encryption_configuration.html">server-side encryption (SSE)</link>.
+      This is not an end-to-end encryption, but can be used to encrypt files that will be persisted
+      to external storage such as S3. Please note that this won't work anymore when using OpenSSL 3
+      for PHP's openssl extension because this is implemented using the legacy cipher RC4.
+      If <xref linkend="opt-system.stateVersion" /> is <emphasis>above</emphasis> <literal>22.05</literal>,
+      this is disabled by default. To turn it on again and for further information please refer to
+      <xref linkend="opt-services.nextcloud.enableBrokenCiphersForSSE" />.
+     </para>
+    </formalpara>
+   </listitem>
   </itemizedlist>
  </section>
 
diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix
index 44faa19bc22ab..a6ab3053087c1 100644
--- a/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -590,9 +590,9 @@ in
           };
         };
         kwinrc = {
-          Windows = {
-            # Forces windows to be maximized
-            Placement = lib.mkDefault "Maximizing";
+          "Wayland" = {
+            "InputMethod[$e]" = "/run/current-system/sw/share/applications/com.github.maliit.keyboard.desktop";
+            "VirtualKeyboardEnabled" = "true";
           };
           "org.kde.kdecoration2" = {
             # No decorations (title bar)
diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh
index 994aa0e33cbfa..4596c160a9571 100644
--- a/nixos/modules/system/boot/stage-1-init.sh
+++ b/nixos/modules/system/boot/stage-1-init.sh
@@ -342,6 +342,14 @@ checkFS() {
     return 0
 }
 
+escapeFstab() {
+    local original="$1"
+
+    # Replace space
+    local escaped="${original// /\\040}"
+    # Replace tab
+    echo "${escaped//$'\t'/\\011}"
+}
 
 # Function for mounting a file system.
 mountFS() {
@@ -569,7 +577,7 @@ while read -u 3 mountPoint; do
         continue
     fi
 
-    mountFS "$device" "$mountPoint" "$options" "$fsType"
+    mountFS "$device" "$(escapeFstab "$mountPoint")" "$(escapeFstab "$options")" "$fsType"
 done
 
 exec 3>&-
diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix
index 399ea9eabe08d..7ab8f8dc676c7 100644
--- a/nixos/modules/tasks/filesystems.nix
+++ b/nixos/modules/tasks/filesystems.nix
@@ -167,7 +167,7 @@ let
          else throw "No device specified for mount point ‘${fs.mountPoint}’.")
       + " " + escape (rootPrefix + fs.mountPoint)
       + " " + fs.fsType
-      + " " + builtins.concatStringsSep "," (fs.options ++ (extraOpts fs))
+      + " " + escape (builtins.concatStringsSep "," (fs.options ++ (extraOpts fs)))
       + " " + (optionalString (!excludeChecks)
         ("0 " + (if skipCheck fs then "0" else if fs.mountPoint == "/" then "1" else "2")))
       + "\n"
diff --git a/nixos/modules/tasks/filesystems/ext.nix b/nixos/modules/tasks/filesystems/ext.nix
index 9b61f21643aba..edc0efc552136 100644
--- a/nixos/modules/tasks/filesystems/ext.nix
+++ b/nixos/modules/tasks/filesystems/ext.nix
@@ -3,13 +3,14 @@
 let
 
   inInitrd = lib.any (fs: fs == "ext2" || fs == "ext3" || fs == "ext4") config.boot.initrd.supportedFilesystems;
+  inSystem = lib.any (fs: fs == "ext2" || fs == "ext3" || fs == "ext4") config.boot.supportedFilesystems;
 
 in
 
 {
   config = {
 
-    system.fsPackages = lib.mkIf (config.boot.initrd.systemd.enable -> inInitrd) [ pkgs.e2fsprogs ];
+    system.fsPackages = lib.mkIf (config.boot.initrd.systemd.enable -> (inInitrd || inSystem)) [ pkgs.e2fsprogs ];
 
     # As of kernel 4.3, there is no separate ext3 driver (they're also handled by ext4.ko)
     boot.initrd.availableKernelModules = lib.mkIf (config.boot.initrd.systemd.enable -> inInitrd) [ "ext2" "ext4" ];
diff --git a/nixos/release-combined.nix b/nixos/release-combined.nix
index bd7b452735f1c..337b5192776f8 100644
--- a/nixos/release-combined.nix
+++ b/nixos/release-combined.nix
@@ -77,6 +77,7 @@ in rec {
         (onFullSupported "nixos.tests.i3wm")
         (onSystems ["x86_64-linux"] "nixos.tests.installer.btrfsSimple")
         (onSystems ["x86_64-linux"] "nixos.tests.installer.btrfsSubvolDefault")
+        (onSystems ["x86_64-linux"] "nixos.tests.installer.btrfsSubvolEscape")
         (onSystems ["x86_64-linux"] "nixos.tests.installer.btrfsSubvols")
         (onSystems ["x86_64-linux"] "nixos.tests.installer.luksroot")
         (onSystems ["x86_64-linux"] "nixos.tests.installer.lvm")
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index bd0fe18c4f8c2..d3dc8b9ca08a4 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -74,6 +74,7 @@ in {
   agda = handleTest ./agda.nix {};
   airsonic = handleTest ./airsonic.nix {};
   allTerminfo = handleTest ./all-terminfo.nix {};
+  alps = handleTest ./alps.nix {};
   amazon-init-shell = handleTest ./amazon-init-shell.nix {};
   apfs = handleTest ./apfs.nix {};
   apparmor = handleTest ./apparmor.nix {};
@@ -251,8 +252,8 @@ in {
   haproxy = handleTest ./haproxy.nix {};
   hardened = handleTest ./hardened.nix {};
   healthchecks = handleTest ./web-apps/healthchecks.nix {};
-  hbase1 = handleTest ./hbase.nix { package=pkgs.hbase1; };
   hbase2 = handleTest ./hbase.nix { package=pkgs.hbase2; };
+  hbase_2_4 = handleTest ./hbase.nix { package=pkgs.hbase_2_4; };
   hbase3 = handleTest ./hbase.nix { package=pkgs.hbase3; };
   hedgedoc = handleTest ./hedgedoc.nix {};
   herbstluftwm = handleTest ./herbstluftwm.nix {};
@@ -490,9 +491,11 @@ in {
   pgadmin4-standalone = handleTest ./pgadmin4-standalone.nix {};
   pgjwt = handleTest ./pgjwt.nix {};
   pgmanage = handleTest ./pgmanage.nix {};
+  phosh = handleTest ./phosh.nix {};
   php = handleTest ./php {};
   php80 = handleTest ./php { php = pkgs.php80; };
   php81 = handleTest ./php { php = pkgs.php81; };
+  php82 = handleTest ./php { php = pkgs.php82; };
   phylactery = handleTest ./web-apps/phylactery.nix {};
   pict-rs = handleTest ./pict-rs.nix {};
   pinnwand = handleTest ./pinnwand.nix {};
diff --git a/nixos/tests/alps.nix b/nixos/tests/alps.nix
new file mode 100644
index 0000000000000..8d7814117df1e
--- /dev/null
+++ b/nixos/tests/alps.nix
@@ -0,0 +1,104 @@
+let
+  certs = import ./common/acme/server/snakeoil-certs.nix;
+  domain = certs.domain;
+in
+import ./make-test-python.nix ({ pkgs, ... }: {
+  name = "alps";
+
+  nodes = {
+    server = {
+      imports = [ ./common/user-account.nix ];
+      security.pki.certificateFiles = [
+        certs.ca.cert
+      ];
+      networking.extraHosts = ''
+        127.0.0.1 ${domain}
+      '';
+      networking.firewall.allowedTCPPorts = [ 25 465 993 ];
+      services.postfix = {
+        enable = true;
+        enableSubmission = true;
+        enableSubmissions = true;
+        tlsTrustedAuthorities = "${certs.ca.cert}";
+        sslCert = "${certs.${domain}.cert}";
+        sslKey = "${certs.${domain}.key}";
+      };
+      services.dovecot2 = {
+        enable = true;
+        enableImap = true;
+        sslCACert = "${certs.ca.cert}";
+        sslServerCert = "${certs.${domain}.cert}";
+        sslServerKey = "${certs.${domain}.key}";
+      };
+    };
+
+    client = { nodes, config, ... }: {
+      security.pki.certificateFiles = [
+        certs.ca.cert
+      ];
+      networking.extraHosts = ''
+        ${nodes.server.config.networking.primaryIPAddress} ${domain}
+      '';
+      services.alps = {
+        enable = true;
+        theme = "alps";
+        imaps = {
+          host = domain;
+          port = 993;
+        };
+        smtps = {
+          host = domain;
+          port = 465;
+        };
+      };
+      environment.systemPackages = [
+        (pkgs.writers.writePython3Bin "test-alps-login" { } ''
+          from urllib.request import build_opener, HTTPCookieProcessor, Request
+          from urllib.parse import urlencode, urljoin
+          from http.cookiejar import CookieJar
+
+          baseurl = "http://localhost:${toString config.services.alps.port}"
+          username = "alice"
+          password = "${nodes.server.config.users.users.alice.password}"
+          cookiejar = CookieJar()
+          cookieprocessor = HTTPCookieProcessor(cookiejar)
+          opener = build_opener(cookieprocessor)
+
+          data = urlencode({"username": username, "password": password}).encode()
+          req = Request(urljoin(baseurl, "login"), data=data, method="POST")
+          with opener.open(req) as ret:
+              # Check that the alps_session cookie is set
+              print(cookiejar)
+              assert any(cookie.name == "alps_session" for cookie in cookiejar)
+
+          req = Request(baseurl)
+          with opener.open(req) as ret:
+              # Check that the alps_session cookie is still there...
+              print(cookiejar)
+              assert any(cookie.name == "alps_session" for cookie in cookiejar)
+              # ...and that we have not been redirected back to the login page
+              print(ret.url)
+              assert ret.url == urljoin(baseurl, "mailbox/INBOX")
+
+          req = Request(urljoin(baseurl, "logout"))
+          with opener.open(req) as ret:
+              # Check that the alps_session cookie is now gone
+              print(cookiejar)
+              assert all(cookie.name != "alps_session" for cookie in cookiejar)
+        '')
+      ];
+    };
+  };
+
+  testScript = ''
+    server.start()
+    server.wait_for_unit("postfix.service")
+    server.wait_for_unit("dovecot2.service")
+    server.wait_for_open_port(465)
+    server.wait_for_open_port(993)
+
+    client.start()
+    client.wait_for_unit("alps.service")
+    client.succeed("test-alps-login")
+  '';
+})
diff --git a/nixos/tests/common/acme/server/acme.test.cert.pem b/nixos/tests/common/acme/server/acme.test.cert.pem
index 76b0d916a8175..562e7a329b681 100644
--- a/nixos/tests/common/acme/server/acme.test.cert.pem
+++ b/nixos/tests/common/acme/server/acme.test.cert.pem
@@ -1,19 +1,19 @@
 -----BEGIN CERTIFICATE-----
-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
+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
-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
+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
 -----END CERTIFICATE-----
diff --git a/nixos/tests/common/acme/server/acme.test.key.pem b/nixos/tests/common/acme/server/acme.test.key.pem
index 741df99a372e3..fd3e9f7dbcf61 100644
--- a/nixos/tests/common/acme/server/acme.test.key.pem
+++ b/nixos/tests/common/acme/server/acme.test.key.pem
@@ -1,27 +1,27 @@
 -----BEGIN RSA PRIVATE KEY-----
-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
+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
 -----END RSA PRIVATE KEY-----
diff --git a/nixos/tests/common/acme/server/ca.cert.pem b/nixos/tests/common/acme/server/ca.cert.pem
index 5c33e879b675b..8d52a0a8f462c 100644
--- a/nixos/tests/common/acme/server/ca.cert.pem
+++ b/nixos/tests/common/acme/server/ca.cert.pem
@@ -1,20 +1,20 @@
 -----BEGIN CERTIFICATE-----
-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
+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
-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
+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
 -----END CERTIFICATE-----
diff --git a/nixos/tests/common/acme/server/ca.key.pem b/nixos/tests/common/acme/server/ca.key.pem
index ed46f5dccf467..cde4e8ac7c758 100644
--- a/nixos/tests/common/acme/server/ca.key.pem
+++ b/nixos/tests/common/acme/server/ca.key.pem
@@ -1,27 +1,27 @@
 -----BEGIN RSA PRIVATE KEY-----
-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
+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
 -----END RSA PRIVATE KEY-----
diff --git a/nixos/tests/deluge.nix b/nixos/tests/deluge.nix
index 0cd1d21870adf..e8945fdea003a 100644
--- a/nixos/tests/deluge.nix
+++ b/nixos/tests/deluge.nix
@@ -54,8 +54,10 @@ import ./make-test-python.nix ({ pkgs, ...} : {
     declarative.wait_for_unit("deluged")
     declarative.wait_for_unit("delugeweb")
     declarative.wait_until_succeeds("curl --fail http://declarative:3142")
+
+    # deluge-console always exits with 1. https://dev.deluge-torrent.org/ticket/3291
     declarative.succeed(
-        "deluge-console 'connect 127.0.0.1:58846 andrew password; help' | grep -q 'rm.*Remove a torrent'"
+        "(deluge-console 'connect 127.0.0.1:58846 andrew password; help' || true) | grep -q 'rm.*Remove a torrent'"
     )
   '';
 })
diff --git a/nixos/tests/grafana/provision/default.nix b/nixos/tests/grafana/provision/default.nix
index 7a707ab9fed1f..1eb927632eb7a 100644
--- a/nixos/tests/grafana/provision/default.nix
+++ b/nixos/tests/grafana/provision/default.nix
@@ -17,7 +17,7 @@ let
 
         security = {
           admin_user = "testadmin";
-          admin_password = "snakeoilpwd";
+          admin_password = "$__file{${pkgs.writeText "pwd" "snakeoilpwd"}}";
         };
       };
     };
@@ -28,17 +28,24 @@ let
   };
 
   extraNodeConfs = {
-    provisionOld = {
+    provisionLegacyNotifiers = {
       services.grafana.provision = {
-        datasources = [{
-          name = "Test Datasource";
-          type = "testdata";
-          access = "proxy";
-          uid = "test_datasource";
-        }];
-
-        dashboards = [{ options.path = "/var/lib/grafana/dashboards"; }];
-
+        datasources.settings = {
+          apiVersion = 1;
+          datasources = [{
+            name = "Test Datasource";
+            type = "testdata";
+            access = "proxy";
+            uid = "test_datasource";
+          }];
+        };
+        dashboards.settings = {
+          apiVersion = 1;
+          providers = [{
+            name = "default";
+            options.path = "/var/lib/grafana/dashboards";
+          }];
+        };
         notifiers = [{
           uid = "test_notifiers";
           name = "Test Notifiers";
@@ -50,7 +57,6 @@ let
         }];
       };
     };
-
     provisionNix = {
       services.grafana.provision = {
         datasources.settings = {
@@ -157,6 +163,22 @@ let
         };
       };
     };
+
+    provisionYamlDirs = let
+      mkdir = p: pkgs.writeTextDir (baseNameOf p) (builtins.readFile p);
+    in {
+      services.grafana.provision = {
+        datasources.path = mkdir ./datasources.yaml;
+        dashboards.path = mkdir ./dashboards.yaml;
+        alerting = {
+          rules.path = mkdir ./rules.yaml;
+          contactPoints.path = mkdir ./contact-points.yaml;
+          policies.path = mkdir ./policies.yaml;
+          templates.path = mkdir ./templates.yaml;
+          muteTimings.path = mkdir ./mute-timings.yaml;
+        };
+      };
+    };
   };
 
   nodes = builtins.mapAttrs (_: val: mkMerge [ val baseGrafanaConf ]) extraNodeConfs;
@@ -172,58 +194,58 @@ in {
   testScript = ''
     start_all()
 
-    nodeOld = ("Nix (old format)", provisionOld)
     nodeNix = ("Nix (new format)", provisionNix)
     nodeYaml = ("Nix (YAML)", provisionYaml)
+    nodeYamlDir = ("Nix (YAML in dirs)", provisionYamlDirs)
 
-    for nodeInfo in [nodeOld, nodeNix, nodeYaml]:
-        with subtest(f"Should start provision node: {nodeInfo[0]}"):
-            nodeInfo[1].wait_for_unit("grafana.service")
-            nodeInfo[1].wait_for_open_port(3000)
+    for description, machine in [nodeNix, nodeYaml, nodeYamlDir]:
+        with subtest(f"Should start provision node: {description}"):
+            machine.wait_for_unit("grafana.service")
+            machine.wait_for_open_port(3000)
 
-        with subtest(f"Successful datasource provision with {nodeInfo[0]}"):
-            nodeInfo[1].succeed(
+        with subtest(f"Successful datasource provision with {description}"):
+            machine.succeed(
                 "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/datasources/uid/test_datasource | grep Test\ Datasource"
             )
 
-        with subtest(f"Successful dashboard provision with {nodeInfo[0]}"):
-            nodeInfo[1].succeed(
+        with subtest(f"Successful dashboard provision with {description}"):
+            machine.succeed(
                 "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/dashboards/uid/test_dashboard | grep Test\ Dashboard"
             )
 
-
-
-    with subtest(f"Successful notifiers provision with {nodeOld[0]}"):
-        nodeOld[1].succeed(
-            "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/alert-notifications/uid/test_notifiers | grep Test\ Notifiers"
-        )
-
-
-
-    for nodeInfo in [nodeNix, nodeYaml]:
-        with subtest(f"Successful rule provision with {nodeInfo[0]}"):
-            nodeInfo[1].succeed(
+        with subtest(f"Successful rule provision with {description}"):
+            machine.succeed(
                 "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/v1/provisioning/alert-rules/test_rule | grep Test\ Rule"
             )
 
-        with subtest(f"Successful contact point provision with {nodeInfo[0]}"):
-            nodeInfo[1].succeed(
+        with subtest(f"Successful contact point provision with {description}"):
+            machine.succeed(
                 "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/v1/provisioning/contact-points | grep Test\ Contact\ Point"
             )
 
-        with subtest(f"Successful policy provision with {nodeInfo[0]}"):
-            nodeInfo[1].succeed(
+        with subtest(f"Successful policy provision with {description}"):
+            machine.succeed(
                 "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/v1/provisioning/policies | grep Test\ Contact\ Point"
             )
 
-        with subtest(f"Successful template provision with {nodeInfo[0]}"):
-            nodeInfo[1].succeed(
+        with subtest(f"Successful template provision with {description}"):
+            machine.succeed(
                 "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/v1/provisioning/templates | grep Test\ Template"
             )
 
-        with subtest("Successful mute timings provision with {nodeInfo[0]}"):
-            nodeInfo[1].succeed(
+        with subtest("Successful mute timings provision with {description}"):
+            machine.succeed(
                 "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/v1/provisioning/mute-timings | grep Test\ Mute\ Timing"
             )
+
+    with subtest("Successful notifiers provision"):
+        provisionLegacyNotifiers.wait_for_unit("grafana.service")
+        provisionLegacyNotifiers.wait_for_open_port(3000)
+        print(provisionLegacyNotifiers.succeed(
+            "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/alert-notifications/uid/test_notifiers"
+        ))
+        provisionLegacyNotifiers.succeed(
+            "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/alert-notifications/uid/test_notifiers | grep Test\ Notifiers"
+        )
   '';
 })
diff --git a/nixos/tests/hibernate.nix b/nixos/tests/hibernate.nix
index 7a4b331169a39..cb75322ca5f92 100644
--- a/nixos/tests/hibernate.nix
+++ b/nixos/tests/hibernate.nix
@@ -26,8 +26,9 @@ let
 
     powerManagement.resumeCommands = "systemctl --no-block restart backdoor.service";
 
-    fileSystems = {
-      "/".device = "/dev/vda2";
+    fileSystems."/" = {
+      device = "/dev/vda2";
+      fsType = "ext3";
     };
     swapDevices = mkOverride 0 [ { device = "/dev/vda1"; } ];
     boot.resumeDevice = mkIf systemdStage1 "/dev/vda1";
diff --git a/nixos/tests/installer-systemd-stage-1.nix b/nixos/tests/installer-systemd-stage-1.nix
index d02387ee80e09..03f0ec8d746b8 100644
--- a/nixos/tests/installer-systemd-stage-1.nix
+++ b/nixos/tests/installer-systemd-stage-1.nix
@@ -8,9 +8,10 @@
   # them when fixed.
   inherit (import ./installer.nix { inherit system config pkgs; systemdStage1 = true; })
     # bcache
-    # btrfsSimple
-    # btrfsSubvolDefault
-    # btrfsSubvols
+    btrfsSimple
+    btrfsSubvolDefault
+    btrfsSubvolEscape
+    btrfsSubvols
     # encryptedFSWithKeyfile
     # grub1
     # luksroot
diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix
index d9f64a781c57e..9b3c8a762991b 100644
--- a/nixos/tests/installer.nix
+++ b/nixos/tests/installer.nix
@@ -911,4 +911,25 @@ in {
       )
     '';
   };
+
+  # Test to see if we can deal with subvols that need to be escaped in fstab
+  btrfsSubvolEscape = makeInstallerTest "btrfsSubvolEscape" {
+    createPartitions = ''
+      machine.succeed(
+          "sgdisk -Z /dev/vda",
+          "sgdisk -n 1:0:+1M -n 2:0:+1G -N 3 -t 1:ef02 -t 2:8200 -t 3:8300 -c 3:root /dev/vda",
+          "mkswap /dev/vda2 -L swap",
+          "swapon -L swap",
+          "mkfs.btrfs -L root /dev/vda3",
+          "btrfs device scan",
+          "mount LABEL=root /mnt",
+          "btrfs subvol create '/mnt/nixos in space'",
+          "btrfs subvol create /mnt/boot",
+          "umount /mnt",
+          "mount -o 'defaults,subvol=nixos in space' LABEL=root /mnt",
+          "mkdir /mnt/boot",
+          "mount -o defaults,subvol=boot LABEL=root /mnt/boot",
+      )
+    '';
+  };
 }
diff --git a/nixos/tests/keycloak.nix b/nixos/tests/keycloak.nix
index 6ce136330d438..228e57d1cdd6f 100644
--- a/nixos/tests/keycloak.nix
+++ b/nixos/tests/keycloak.nix
@@ -5,10 +5,13 @@
 let
   certs = import ./common/acme/server/snakeoil-certs.nix;
   frontendUrl = "https://${certs.domain}";
-  initialAdminPassword = "h4IhoJFnt2iQIR9";
 
   keycloakTest = import ./make-test-python.nix (
     { pkgs, databaseType, ... }:
+    let
+      initialAdminPassword = "h4Iho\"JFn't2>iQIR9";
+      adminPasswordFile = pkgs.writeText "admin-password" "${initialAdminPassword}";
+    in
     {
       name = "keycloak";
       meta = with pkgs.lib.maintainers; {
@@ -37,7 +40,7 @@ let
               type = databaseType;
               username = "bogus";
               name = "also bogus";
-              passwordFile = "${pkgs.writeText "dbPassword" "wzf6vOCbPp6cqTH"}";
+              passwordFile = "${pkgs.writeText "dbPassword" ''wzf6\"vO"Cb\nP>p#6;c&o?eu=q'THE'''H''''E''}";
             };
             plugins = with config.services.keycloak.package.plugins; [
               keycloak-discord
@@ -111,7 +114,7 @@ let
           keycloak.succeed("""
               curl -sSf -d 'client_id=admin-cli' \
                    -d 'username=admin' \
-                   -d 'password=${initialAdminPassword}' \
+                   -d "password=$(<${adminPasswordFile})" \
                    -d 'grant_type=password' \
                    '${frontendUrl}/realms/master/protocol/openid-connect/token' \
                    | jq -r '"Authorization: bearer " + .access_token' >admin_auth_header
@@ -119,10 +122,10 @@ let
 
           # Register the metrics SPI
           keycloak.succeed(
-              "${pkgs.jre}/bin/keytool -import -alias snakeoil -file ${certs.ca.cert} -storepass aaaaaa -keystore cacert.jks -noprompt",
-              "KC_OPTS='-Djavax.net.ssl.trustStore=cacert.jks -Djavax.net.ssl.trustStorePassword=aaaaaa' kcadm.sh config credentials --server '${frontendUrl}' --realm master --user admin --password '${initialAdminPassword}'",
-              "KC_OPTS='-Djavax.net.ssl.trustStore=cacert.jks -Djavax.net.ssl.trustStorePassword=aaaaaa' kcadm.sh update events/config -s 'eventsEnabled=true' -s 'adminEventsEnabled=true' -s 'eventsListeners+=metrics-listener'",
-              "curl -sSf '${frontendUrl}/realms/master/metrics' | grep '^keycloak_admin_event_UPDATE'"
+              """${pkgs.jre}/bin/keytool -import -alias snakeoil -file ${certs.ca.cert} -storepass aaaaaa -keystore cacert.jks -noprompt""",
+              """KC_OPTS='-Djavax.net.ssl.trustStore=cacert.jks -Djavax.net.ssl.trustStorePassword=aaaaaa' kcadm.sh config credentials --server '${frontendUrl}' --realm master --user admin --password "$(<${adminPasswordFile})" """,
+              """KC_OPTS='-Djavax.net.ssl.trustStore=cacert.jks -Djavax.net.ssl.trustStorePassword=aaaaaa' kcadm.sh update events/config -s 'eventsEnabled=true' -s 'adminEventsEnabled=true' -s 'eventsListeners+=metrics-listener'""",
+              """curl -sSf '${frontendUrl}/realms/master/metrics' | grep '^keycloak_admin_event_UPDATE'"""
           )
 
           # Publish the realm, including a test OIDC client and user
diff --git a/nixos/tests/nextcloud/basic.nix b/nixos/tests/nextcloud/basic.nix
index eb37470a4c7bb..a475049e7b264 100644
--- a/nixos/tests/nextcloud/basic.nix
+++ b/nixos/tests/nextcloud/basic.nix
@@ -37,6 +37,8 @@ in {
         "d /var/lib/nextcloud-data 0750 nextcloud nginx - -"
       ];
 
+      system.stateVersion = "22.11"; # stateVersion >=21.11 to make sure that we use OpenSSL3
+
       services.nextcloud = {
         enable = true;
         datadir = "/var/lib/nextcloud-data";
@@ -99,6 +101,10 @@ in {
     # This is just to ensure the nextcloud-occ program is working
     nextcloud.succeed("nextcloud-occ status")
     nextcloud.succeed("curl -sSf http://nextcloud/login")
+    # Ensure that no OpenSSL 1.1 is used.
+    nextcloud.succeed(
+        "${nodes.nextcloud.services.phpfpm.pools.nextcloud.phpPackage}/bin/php -i | grep 'OpenSSL Library Version' | awk -F'=>' '{ print $2 }' | awk '{ print $2 }' | grep -v 1.1"
+    )
     nextcloud.succeed(
         "${withRcloneEnv} ${copySharedFile}"
     )
@@ -108,5 +114,6 @@ in {
         "${withRcloneEnv} ${diffSharedFile}"
     )
     assert "hi" in client.succeed("cat /mnt/dav/test-shared-file")
+    nextcloud.succeed("grep -vE '^HBEGIN:oc_encryption_module' /var/lib/nextcloud-data/data/root/files/test-shared-file")
   '';
 })) args
diff --git a/nixos/tests/nextcloud/default.nix b/nixos/tests/nextcloud/default.nix
index 7dbdff9882387..b8d3ba75b51a9 100644
--- a/nixos/tests/nextcloud/default.nix
+++ b/nixos/tests/nextcloud/default.nix
@@ -8,6 +8,10 @@ with pkgs.lib;
 foldl
   (matrix: ver: matrix // {
     "basic${toString ver}" = import ./basic.nix { inherit system pkgs; nextcloudVersion = ver; };
+    "openssl-sse${toString ver}" = import ./openssl-sse.nix {
+      inherit system pkgs;
+      nextcloudVersion = ver;
+    };
     "with-postgresql-and-redis${toString ver}" = import ./with-postgresql-and-redis.nix {
       inherit system pkgs;
       nextcloudVersion = ver;
diff --git a/nixos/tests/nextcloud/openssl-sse.nix b/nixos/tests/nextcloud/openssl-sse.nix
new file mode 100644
index 0000000000000..7595ee2c67e31
--- /dev/null
+++ b/nixos/tests/nextcloud/openssl-sse.nix
@@ -0,0 +1,105 @@
+args@{ pkgs, nextcloudVersion ? 25, ... }:
+
+(import ../make-test-python.nix ({ pkgs, ...}: let
+  adminuser = "root";
+  adminpass = "notproduction";
+  nextcloudBase = {
+    networking.firewall.allowedTCPPorts = [ 80 ];
+    system.stateVersion = "22.05"; # stateVersions <22.11 use openssl 1.1 by default
+    services.nextcloud = {
+      enable = true;
+      config.adminpassFile = "${pkgs.writeText "adminpass" adminpass}";
+      package = pkgs.${"nextcloud" + (toString nextcloudVersion)};
+    };
+  };
+in {
+  name = "nextcloud-openssl";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ ma27 ];
+  };
+  nodes.nextcloudwithopenssl1 = {
+    imports = [ nextcloudBase ];
+    services.nextcloud.hostName = "nextcloudwithopenssl1";
+  };
+  nodes.nextcloudwithopenssl3 = {
+    imports = [ nextcloudBase ];
+    services.nextcloud = {
+      hostName = "nextcloudwithopenssl3";
+      enableBrokenCiphersForSSE = false;
+    };
+  };
+  testScript = { nodes, ... }: let
+    withRcloneEnv = host: pkgs.writeScript "with-rclone-env" ''
+      #!${pkgs.runtimeShell}
+      export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav
+      export RCLONE_CONFIG_NEXTCLOUD_URL="http://${host}/remote.php/webdav/"
+      export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud"
+      export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}"
+      export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})"
+      "''${@}"
+    '';
+    withRcloneEnv1 = withRcloneEnv "nextcloudwithopenssl1";
+    withRcloneEnv3 = withRcloneEnv "nextcloudwithopenssl3";
+    copySharedFile1 = pkgs.writeScript "copy-shared-file" ''
+      #!${pkgs.runtimeShell}
+      echo 'hi' | ${withRcloneEnv1} ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file
+    '';
+    copySharedFile3 = pkgs.writeScript "copy-shared-file" ''
+      #!${pkgs.runtimeShell}
+      echo 'bye' | ${withRcloneEnv3} ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file2
+    '';
+    openssl1-node = nodes.nextcloudwithopenssl1.config.system.build.toplevel;
+    openssl3-node = nodes.nextcloudwithopenssl3.config.system.build.toplevel;
+  in ''
+    nextcloudwithopenssl1.start()
+    nextcloudwithopenssl1.wait_for_unit("multi-user.target")
+    nextcloudwithopenssl1.succeed("nextcloud-occ status")
+    nextcloudwithopenssl1.succeed("curl -sSf http://nextcloudwithopenssl1/login")
+
+    with subtest("With OpenSSL 1 SSE can be enabled and used"):
+        nextcloudwithopenssl1.succeed("nextcloud-occ app:enable encryption")
+        nextcloudwithopenssl1.succeed("nextcloud-occ encryption:enable")
+
+    with subtest("Upload file and ensure it's encrypted"):
+        nextcloudwithopenssl1.succeed("${copySharedFile1}")
+        nextcloudwithopenssl1.succeed("grep -E '^HBEGIN:oc_encryption_module' /var/lib/nextcloud/data/root/files/test-shared-file")
+        nextcloudwithopenssl1.succeed("${withRcloneEnv1} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file | grep hi")
+
+    with subtest("Switch to OpenSSL 3"):
+        nextcloudwithopenssl1.succeed("${openssl3-node}/bin/switch-to-configuration test")
+        nextcloudwithopenssl1.wait_for_open_port(80)
+        nextcloudwithopenssl1.succeed("nextcloud-occ status")
+
+    with subtest("Existing encrypted files cannot be read, but new files can be added"):
+        nextcloudwithopenssl1.fail("${withRcloneEnv3} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file >&2")
+        nextcloudwithopenssl1.succeed("nextcloud-occ encryption:disable")
+        nextcloudwithopenssl1.succeed("${copySharedFile3}")
+        nextcloudwithopenssl1.succeed("grep bye /var/lib/nextcloud/data/root/files/test-shared-file2")
+        nextcloudwithopenssl1.succeed("${withRcloneEnv3} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file2 | grep bye")
+
+    with subtest("Switch back to OpenSSL 1.1 and ensure that encrypted files are readable again"):
+        nextcloudwithopenssl1.succeed("${openssl1-node}/bin/switch-to-configuration test")
+        nextcloudwithopenssl1.wait_for_open_port(80)
+        nextcloudwithopenssl1.succeed("nextcloud-occ status")
+        nextcloudwithopenssl1.succeed("nextcloud-occ encryption:enable")
+        nextcloudwithopenssl1.succeed("${withRcloneEnv1} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file2 | grep bye")
+        nextcloudwithopenssl1.succeed("${withRcloneEnv1} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file | grep hi")
+        nextcloudwithopenssl1.succeed("grep -E '^HBEGIN:oc_encryption_module' /var/lib/nextcloud/data/root/files/test-shared-file")
+        nextcloudwithopenssl1.succeed("grep bye /var/lib/nextcloud/data/root/files/test-shared-file2")
+
+    with subtest("Ensure that everything can be decrypted"):
+        nextcloudwithopenssl1.succeed("echo y | nextcloud-occ encryption:decrypt-all >&2")
+        nextcloudwithopenssl1.succeed("${withRcloneEnv1} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file2 | grep bye")
+        nextcloudwithopenssl1.succeed("${withRcloneEnv1} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file | grep hi")
+        nextcloudwithopenssl1.succeed("grep -vE '^HBEGIN:oc_encryption_module' /var/lib/nextcloud/data/root/files/test-shared-file")
+
+    with subtest("Switch to OpenSSL 3 ensure that all files are usable now"):
+        nextcloudwithopenssl1.succeed("${openssl3-node}/bin/switch-to-configuration test")
+        nextcloudwithopenssl1.wait_for_open_port(80)
+        nextcloudwithopenssl1.succeed("nextcloud-occ status")
+        nextcloudwithopenssl1.succeed("${withRcloneEnv3} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file2 | grep bye")
+        nextcloudwithopenssl1.succeed("${withRcloneEnv3} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file | grep hi")
+
+    nextcloudwithopenssl1.shutdown()
+  '';
+})) args
diff --git a/nixos/tests/phosh.nix b/nixos/tests/phosh.nix
new file mode 100644
index 0000000000000..6c6357f580968
--- /dev/null
+++ b/nixos/tests/phosh.nix
@@ -0,0 +1,65 @@
+import ./make-test-python.nix ({ pkgs, ...}: let
+  pin = "1234";
+in {
+  name = "phosh";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ zhaofengli ];
+  };
+
+  nodes = {
+    phone = { config, pkgs, ... }: {
+      users.users.nixos = {
+        isNormalUser = true;
+        password = pin;
+      };
+
+      services.xserver.desktopManager.phosh = {
+        enable = true;
+        user = "nixos";
+        group = "users";
+
+        phocConfig = {
+          outputs.Virtual-1 = {
+            scale = 2;
+          };
+        };
+      };
+
+      systemd.services.phosh = {
+        environment = {
+          # Accelerated graphics fail on phoc 0.20 (wlroots 0.15)
+          "WLR_RENDERER" = "pixman";
+        };
+      };
+
+      virtualisation.resolution = { x = 720; y = 1440; };
+      virtualisation.qemu.options = [ "-vga none -device virtio-gpu-pci,xres=720,yres=1440" ];
+    };
+  };
+
+  enableOCR = true;
+
+  testScript = ''
+    import time
+
+    start_all()
+    phone.wait_for_unit("phosh.service")
+
+    with subtest("Check that we can see the lock screen info page"):
+        # Saturday, January 1
+        phone.succeed("timedatectl set-time '2022-01-01 07:00'")
+
+        phone.wait_for_text("Saturday")
+        phone.screenshot("01lockinfo")
+
+    with subtest("Check that we can unlock the screen"):
+        phone.send_chars("${pin}", delay=0.2)
+        time.sleep(1)
+        phone.screenshot("02unlock")
+
+        phone.send_chars("\n")
+
+        phone.wait_for_text("All Apps")
+        phone.screenshot("03launcher")
+  '';
+})
diff --git a/nixos/tests/systemd-initrd-luks-password.nix b/nixos/tests/systemd-initrd-luks-password.nix
index e8e651f7b35f8..55d0b4324b400 100644
--- a/nixos/tests/systemd-initrd-luks-password.nix
+++ b/nixos/tests/systemd-initrd-luks-password.nix
@@ -23,6 +23,8 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
         cryptroot2.device = "/dev/vdd";
       };
       virtualisation.bootDevice = "/dev/mapper/cryptroot";
+      # test mounting device unlocked in initrd after switching root
+      virtualisation.fileSystems."/cryptroot2".device = "/dev/mapper/cryptroot2";
     };
   };
 
@@ -31,6 +33,8 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
     machine.wait_for_unit("multi-user.target")
     machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdc -")
     machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdd -")
+    machine.succeed("echo -n supersecret | cryptsetup luksOpen   -q               /dev/vdd cryptroot2")
+    machine.succeed("mkfs.ext4 /dev/mapper/cryptroot2")
 
     # Boot from the encrypted disk
     machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-luks.conf")
@@ -44,5 +48,6 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
     machine.wait_for_unit("multi-user.target")
 
     assert "/dev/mapper/cryptroot on / type ext4" in machine.succeed("mount")
+    assert "/dev/mapper/cryptroot2 on /cryptroot2 type ext4" in machine.succeed("mount")
   '';
 })
diff --git a/nixos/tests/web-apps/mastodon.nix b/nixos/tests/web-apps/mastodon.nix
index 05cc727476d67..bc1122e7268f9 100644
--- a/nixos/tests/web-apps/mastodon.nix
+++ b/nixos/tests/web-apps/mastodon.nix
@@ -16,7 +16,7 @@ let
 in
 {
   name = "mastodon";
-  meta.maintainers = with pkgs.lib.maintainers; [ erictapen izorkin ];
+  meta.maintainers = with pkgs.lib.maintainers; [ erictapen izorkin turion ];
 
   nodes = {
     ca = { pkgs, ... }: {
@@ -56,6 +56,9 @@ in
     };
 
     server = { pkgs, ... }: {
+
+      virtualisation.memorySize = 2048;
+
       networking = {
         interfaces.eth1 = {
           ipv4.addresses = [