Compare commits

..

30 Commits

Author SHA1 Message Date
Justin Bedo
35c83af8b8 R: disable tcltk core test on darwin 2026-08-29 07:24:30 +10:00
Justin Bedo
7fa5f84179 R: use replace-fail for substitutions 2026-08-22 09:00:14 +10:00
Justin Bedo
91d8de0e1b rPackages: clean up installFlags and NIX_CFLAGS_COMPILE 2026-08-20 16:11:56 +10:00
Justin Bedő
befba536a5 R: prepend instead of overriding TCLLIBPATH (#553675) 2026-08-20 04:15:40 +00:00
Justin Bedő
84b2cfe60e rPackages.{mongolite,openssl,websocket}: link openssl via pkg-config (#553654) 2026-08-20 04:15:18 +00:00
TomaSajt
8ade1f069e rPackages: properly handle passed buildInputs 2026-08-19 10:48:42 +02:00
Justin Bedo
3921cb2f52 rPackages.ModelMetrics: fix missing openmp dependency 2026-08-19 15:12:39 +10:00
Justin Bedo
c4f343e091 rPackages.data_table: fix missing openmp dependency 2026-08-19 15:12:39 +10:00
Justin Bedo
f12c2fc819 rPackages: include libintl on darwin 2026-08-19 15:12:38 +10:00
TomaSajt
180812aa8b rPackages.rpanel: add tclPackages.bwidget to search path on load 2026-08-17 19:01:23 +02:00
TomaSajt
99e50baea2 R: prepend instead of overriding TCLLIBPATH 2026-08-17 18:31:22 +02:00
TomaSajt
5e7cdb6263 rPackages.{mongolite,openssl,websocket}: link openssl via pkg-config 2026-08-17 15:06:36 +02:00
TomaSajt
375523bb1a rPackages.immunotation: clean up patches 2026-08-17 16:22:49 +10:00
TomaSajt
3300252793 rPackages.Rserve: use symlink instead of copying to $out 2026-08-17 16:22:49 +10:00
TomaSajt
9e4efccb2b rPackages.httpuv: use system libuv 2026-08-17 16:22:49 +10:00
Justin Bedő
e15bd38348 rPackages: strip binaries in common locations (#552718) 2026-08-17 06:11:04 +00:00
Justin Bedő
d0d54e1ad4 rPackages.buildRPackage: remove unneeded deps (#552702) 2026-08-17 06:10:14 +00:00
TomaSajt
16eadf108d rPackages: add more empty list default logic 2026-08-15 12:09:05 +02:00
TomaSajt
1966470456 rPackages.buildRPackage: remove unneeded deps 2026-08-15 11:53:59 +02:00
Justin Bedő
18a6b995b9 rPackages.buildRPackage: fix structuredAttrs support (#552725) 2026-08-15 03:07:36 +00:00
TomaSajt
f2a6351124 rPackages.buildRPackage: fix structuredAttrs support 2026-08-15 01:15:51 +02:00
TomaSajt
cef26c7c61 rPackages: strip binaries in common locations 2026-08-14 19:30:41 +02:00
TomaSajt
fd8ab89e5d rPackages.iscream: remove unused filtering logic 2026-08-14 10:58:09 +02:00
Justin Bedő
c76bc9e733 rPackages: don't put R deps into nativeBuildInputs (#552398) 2026-08-14 00:07:55 +00:00
Justin Bedő
3f975f295a rPackages.iscream: fix build with more robust filtering (#552391) 2026-08-14 00:04:32 +00:00
TomaSajt
3ecb4f82d3 rPackages: don't put R deps into nativeBuildInputs 2026-08-13 21:54:24 +02:00
TomaSajt
dd497f7abd rPackages.iscream: fix build with more robust filtering 2026-08-13 21:44:11 +02:00
TomaSajt
21bc93c967 rPackages.buildRPackage: enable __structuredAttrs 2026-08-12 16:31:08 +02:00
TomaSajt
e34a43b2fa rPackages.buildRPackage: enable strictDeps 2026-08-12 16:30:19 +02:00
TomaSajt
3f79d0dd24 rPackages.buildRPackage: refactor 2026-08-12 16:28:47 +02:00
3617 changed files with 35959 additions and 50205 deletions

20
.github/labeler.yml vendored
View File

@@ -43,6 +43,14 @@
- .github/**/*
- ci/**/*.*
"6.topic: coq":
- any:
- changed-files:
- any-glob-to-any-file:
- pkgs/applications/science/logic/coq/**/*
- pkgs/development/coq-modules/**/*
- pkgs/top-level/coq-packages.nix
"6.topic: COSMIC":
- any:
- changed-files:
@@ -458,18 +466,6 @@
- any-glob-to-any-file:
- pkgs/development/rocm-modules/**/*
"6.topic: rocq":
- any:
- changed-files:
- any-glob-to-any-file:
- pkgs/applications/science/logic/coq/**/*
- pkgs/applications/science/logic/rocq-core/**/*
- pkgs/build-support/coq/**/*
- pkgs/build-support/rocq/**/*
- pkgs/development/rocq-modules/**/*
- pkgs/top-level/coq-packages.nix
- pkgs/top-level/rocq-packages.nix
"6.topic: ruby":
- any:
- changed-files:

View File

@@ -466,8 +466,9 @@ nixos/tests/incus/ @adamcstephens
pkgs/by-name/in/incus/ @adamcstephens
pkgs/by-name/lx/lxc* @adamcstephens
# Flutter
# ExpidusOS, Flutter
/pkgs/development/compilers/flutter @RossComputerGuy
/pkgs/desktops/expidus @RossComputerGuy
# GNU Tar & Zip
/pkgs/by-name/gn/gnutar @RossComputerGuy

View File

@@ -68,7 +68,7 @@ See [](#ex-portableService-hello) to understand how to use the output of `portab
: Allows you to override the package that provides {manpage}`mksquashfs(1)`, which is used internally by `portableService`.
_Default value:_ `pkgs.squashfs-tools`.
_Default value:_ `pkgs.squashfsTools`.
`squash-compression` (String; _optional_)

View File

@@ -206,7 +206,7 @@ Here is how your `default.nix` file would look for a Phoenix project.
# beam27Packages or beam29Packages is available if you need a particular version
beamPackages,
}:
beamPackages.mixRelease (finalAttrs: {
let
pname = "your_project";
version = "0.0.1";
@@ -215,6 +215,24 @@ beamPackages.mixRelease (finalAttrs: {
rev = "replace_with_your_commit";
};
# if using mix2nix you can use the mixNixDeps attribute
mixFodDeps = beamPackages.fetchMixDeps {
pname = "mix-deps-${pname}";
inherit src version;
# nix will complain and tell you the right value to replace this with
hash = lib.fakeHash;
mixEnv = ""; # default is "prod", when empty includes all dependencies, such as "dev", "test".
# if you have build time environment variables add them here
MY_ENV_VAR = "my_value";
};
in
beamPackages.mixRelease {
inherit
src
pname
version
mixFodDeps
;
# if you have build time environment variables add them here
MY_ENV_VAR = "my_value";
@@ -224,18 +242,7 @@ beamPackages.mixRelease (finalAttrs: {
mix do deps.loadpaths --no-deps-check, phx.digest
mix phx.digest --no-deps-check
'';
# if using mix2nix you can use the mixNixDeps attribute
mixFodDeps = beamPackages.fetchMixDeps {
pname = "mix-deps-${finalAttrs.pname}";
inherit (finalAttrs) src version;
# nix will complain and tell you the right value to replace this with
hash = lib.fakeHash;
mixEnv = ""; # default is "prod", when empty includes all dependencies, such as "dev", "test".
# if you have build time environment variables add them here
MY_ENV_VAR = "my_value";
};
})
}
```
Setup will require the following steps:

View File

@@ -59,9 +59,6 @@ Here is a simple package example.
- The library will be installed using the `angstrom.install` file that dune
generates.
- It also accepts an optional `dunePackages` argument, if there is more than one
dune package that needs to be built (see `zipperposition`)
```nix
{
lib,

View File

@@ -3,12 +3,11 @@
Note that "The Rocq Prover" (Rocq for short) is the new name of the
proof assistant formerly known as Coq. The `coq` and `coqPackages`
derivations currently remain for both older versions of Coq, but also
as compatibility aliases for some versions of Rocq. In both cases, the
`coq` and `rocq-core` attributes exist. In the case of Coq (< 9),
`rocq-core` is just an alias for `coq`, while in the case of Rocq (>= 9),
`rocq-core` is the main Rocq derivation, while `coq` provides
compatibility binaries (`coqc`, `coqtop`, etc.) for packages that still
depend on them.
some versions of Rocq during the renaming transition. In the latter
case, the `coq` derivation encompasses the compatibility binaries
(`coqtop`, `coqc`, etc.) in addition to the `rocq` binary. The packages
only in `coqPackages` are the ones which currently still depend on these
compatibility binaries.
## Rocq derivation: `rocq-core` {#rocq-derivation-rocq}
@@ -18,18 +17,18 @@ The Rocq derivation is overridable through the `rocq-core.override overrides`, w
* `customOCamlPackages` (optional, defaults to `null`, which lets Rocq choose a version automatically), which can be set to any of the ocaml packages attribute of `ocaml-ng` (such as `ocaml-ng.ocamlPackages_4_14` which is the default for Rocq 9.1 for example).
* `rocq-version` (optional, defaults to the short version e.g. "9.1"), is a version number of the form "x.y" that indicates which Rocq's version build behavior to mimic when using a source which is not a release. E.g. `rocq-core.override { version = "40be8435e132aab2231a79091f011ebc3e64a753"; rocq-version = "9.1"; }`.
## Creating custom Coq environments with `rocq-core.withPackages` {#coq-withPackages}
## Creating custom Coq environments with `coq.withPackages` {#coq-withPackages}
The `rocq-core.withPackages` function provides a convenient way to create a Rocq environment that includes additional Rocq packages. This is similar to how `python.withPackages` works for Python environments.
The `coq.withPackages` function provides a convenient way to create a Coq environment that includes additional Coq packages. This is similar to how `python.withPackages` works for Python environments.
The function takes a function that receives the Rocq package set and returns a list of packages. It returns a wrapped Rocq environment where the Rocq binaries (`rocq`, etc.) are configured with the appropriate environment variables to find the packages.
The function takes a function that receives the Coq package set and returns a list of packages. It returns a wrapped Coq environment where all Coq binaries (`coqtop`, `coqc`, `coqdep`, `coqchk`, `coqide`, etc.) are configured with the appropriate environment variables to find the packages.
### Usage {#coq-withPackages-usage}
Here is an example of creating a Rocq environment with specific packages.
Here is an example of creating a Coq environment with specific packages.
```nix
rocq-core.withPackages (
coq.withPackages (
ps: with ps; [
mathcomp
bignums
@@ -37,9 +36,7 @@ rocq-core.withPackages (
)
```
If you install the `vsrocq-language-server` or `rocq-lsp` server, make sure to list them as part of the above `rocq-core.withPackages` expression instead of installing them separately if you want them to find your Rocq packages.
For versions prior to Rocq 9.0, a similar `coq.withPackages` function is available.
If you install the `vsrocq-language-server` or `rocq-lsp` server, make sure to list them as part of the above `coq.withPackages` expression instead of installing them separately if you want them to find your Coq/Rocq packages.
## Rocq packages attribute sets: `rocqPackages` {#rocq-packages-attribute-sets-rocqpackages}
@@ -133,7 +130,7 @@ mkRocqDerivation {
mathcomp.boot
mathcomp.algebra
mathcomp-finmap
mathcomp.finite-group
mathcomp.fingroup
mathcomp-bigenough
];

View File

@@ -82,8 +82,6 @@
- `nix-serve-ng` (and `haskellPackages.nix-serve-ng`) is now built against Lix instead of CppNix, following upstream which has switched to Lix as its supported Nix implementation.
- `buildPythonPackage` and `buildPythonApplication` now set `__structuredAttrs = true` by default. You can explicitly set `__structuredAttrs = false` in packages broken by this change.
- Linux kernel configuration has been moved out of the `linux-kernel` field of the platform structure into the kernel builders:
- `linux-kernel.name` has been removed.
- `linux-kernel.target` is available as the `target` parameter and passthru attribute on the kernel builders.
@@ -119,8 +117,6 @@
- `gh-actions-cache` has been removed since its functionality has been integrated directly into `gh` (`gh cache`). See [upstream readme](https://github.com/actions/gh-actions-cache).
- The OCaml-based Xen Store Daemon has been split off the `xen` package, and is now present in the `ocamlPackages.oxenstored` package.
- `requireFile` now sets `meta.license = lib.licenses.unfree` by default. Users of `requireFile`-based derivations that preserve this default will need to explicitly allow their evaluation as described in [](#sec-allow-unfree).
- `texlive.combine` is deprecated and scheduled for removal in 27.05. Please migrate to `texliveSmall.withPackages` (see [](#sec-language-texlive-user-guide)).
@@ -140,8 +136,6 @@
[pnpm `fetcherVersion` section](#javascript-pnpm-fetcherVersion) of the manual
for details.
- `makeSetupHook` now uses structured attributes and only makes substitutions based on the values of the `substitutions` argument - other derivation attributes are no longer considered.
- `rebuilderd` has been updated to 0.27.0 introducing breaking changes. See upstream changelog for details: [0.26.0](https://github.com/kpcyrd/rebuilderd/releases/tag/v0.26.0), [0.27.0](https://github.com/kpcyrd/rebuilderd/releases/tag/v0.27.0)
- Starting with v14, `flameshot` will primarily utilise xdg-desktop-portal calls for screenshotting. This will directly affect users on X11 window managers due to the lack of a compatible portal with Screenshot feature. See [upstream changelog](https://github.com/flameshot-org/flameshot/releases/tag/v14.0.0) or [NixOS Flameshot](https://wiki.nixos.org/wiki/Flameshot) wiki page for workarounds.
@@ -169,8 +163,6 @@
- The fwts efi-runtime kernel module was removed.
- `homebox` v0.26.0 introduced a new, required value to be set, `HBOX_AUTH_API_KEY_PEPPER`. If one is not provided the module will create one, it is recommended that you back this up as it is part of API Key generation and validation.
- Emacs loads the `early-default` library after `early-init.el`.
Users can add `early-init.el` via `emacs.pkgs.withPackages`
by packaging `early-init.el` into a library named `early-default`.

View File

@@ -349,33 +349,24 @@ rec {
```
*/
_normaliseTreeFilter =
let
# Recurses into a tree that's already known to be a directory (either a "directory" or an attrset).
#
# Only directories need to be recursed into:
# Files are either null (excluded) or a file type string (included), which are already normalised.
#
# Checking this in the caller instead of here also avoids the thunk allocation for the path concatenation below.
recurse =
path: tree:
let
normalisedSubtrees = mapAttrs (
name: subtree:
if subtree == "directory" || isAttrs subtree then recurse (path + "/${name}") subtree else subtree
) (_directoryEntries path tree);
subtreeValues = attrValues normalisedSubtrees;
in
# This triggers either when all files in a directory are filtered out
# Or when the directory doesn't contain any files at all
if all isNull subtreeValues then
null
# Triggers when we have the same as a `readDir path`, so we can turn it back into an equivalent "directory".
else if all isString subtreeValues then
"directory"
else
normalisedSubtrees;
in
path: tree: if tree == "directory" || isAttrs tree then recurse path tree else tree;
path: tree:
if tree == "directory" || isAttrs tree then
let
entries = _directoryEntries path tree;
normalisedSubtrees = mapAttrs (name: _normaliseTreeFilter (path + "/${name}")) entries;
subtreeValues = attrValues normalisedSubtrees;
in
# This triggers either when all files in a directory are filtered out
# Or when the directory doesn't contain any files at all
if all isNull subtreeValues then
null
# Triggers when we have the same as a `readDir path`, so we can turn it back into an equivalent "directory".
else if all isString subtreeValues then
"directory"
else
normalisedSubtrees
else
tree;
/**
A minimal normalisation of a filesetTree, intended for pretty-printing:
@@ -535,9 +526,6 @@ rec {
else
"/" + concatStringsSep "/" fileset._internalBaseComponents + "/";
getBaseStringPrefix = substring 0 baseLength;
removeBaseStringPrefix = substring baseLength (-1);
baseLength = stringLength baseString;
# Check whether a list of path components under the base path exists in the tree.
@@ -563,12 +551,7 @@ rec {
# or a string ("directory" or "regular", etc.) in which case it's included
localTree != null;
in
# Start by recursing into the first element. This is guaranteed to be
# safe. components will never be empty (builtins.split can't make an
# empty list). Tree can be something other than an attrset, but if so,
# the isAttrs check will fail when being passed `or tree`, and the index
# being ahead doesn't matter.
recurse 2 (tree.${head components} or tree);
recurse 0 tree;
# Filter suited when there's no files
empty = _: _: false;
@@ -586,34 +569,25 @@ rec {
pathSlash = path + "/";
in
(
# Same as `hasPrefix baseString pathSlash`, but more efficient.
# The path is either the base itself or underneath it,
# but only on the few paths above it, so its checked first.
# With base /foo/bar this matches /foo/bar and /foo/bar/baz
# hasPrefix "/foo/bar/" "/foo/bar/baz/"
if getBaseStringPrefix pathSlash == baseString then
if pathSlash == baseString then
# The path is the base directory itself, which is always included
true
else
# Same as `removePrefix baseString path`, but more efficient.
# From the above code we know that hasPrefix baseString pathSlash holds, so this is safe.
# We don't use pathSlash here because we only needed the trailing slash for the prefix matching.
# With base /foo and path /foo/bar/baz this gives
# inTree (split "/" (removePrefix "/foo/" "/foo/bar/baz"))
# == inTree (split "/" "bar/baz")
# == inTree [ "bar" "baz" ]
inTree (split "/" (removeBaseStringPrefix path))
# Same as `hasPrefix pathSlash baseString`, but more efficient.
# The path is a proper ancestor of the base, which needs to be included for the base to be reachable:
# With base /foo/bar we need to include /foo:
# hasPrefix "/foo/" "/foo/bar/"
else if substring 0 (stringLength pathSlash) baseString == pathSlash then
if substring 0 (stringLength pathSlash) baseString == pathSlash then
true
else
# The path is unrelated to the base, so nothing from it is included
# With base /foo/bar this matches e.g. /baz
# Same as `! hasPrefix baseString pathSlash`, but more efficient.
# With base /foo/bar we need to exclude /baz
# ! hasPrefix "/baz/" "/foo/bar/"
else if substring 0 baseLength pathSlash != baseString then
false
else
# Same as `removePrefix baseString path`, but more efficient.
# From the above code we know that hasPrefix baseString pathSlash holds, so this is safe.
# We don't use pathSlash here because we only needed the trailing slash for the prefix matching.
# With base /foo and path /foo/bar/baz this gives
# inTree (split "/" (removePrefix "/foo/" "/foo/bar/baz"))
# == inTree (split "/" "bar/baz")
# == inTree [ "bar" "baz" ]
inTree (split "/" (substring baseLength (-1) path))
)
# This is a way have an additional check in case the above is true without any significant performance cost
&& (
@@ -828,34 +802,21 @@ rec {
*/
_unionTrees =
trees:
if length trees == 1 then
# The union of a single tree simply returns the first element
head trees
else
let
# Like lib.findFirstIndex but without indexing.
# This is a hot path so the indexing arithmetic adds up.
firstStr = foldl' (
found: tree:
if found != null then
found
else if isString tree then
tree
else
found # null
) null trees;
nonNulls = filter (tree: tree != null) trees;
in
let
stringIndex = findFirstIndex isString null trees;
withoutNull = filter (tree: tree != null) trees;
in
if stringIndex != null then
# If there's a string, it's always a fully included tree (dir or file),
# no need to look at other elements
if firstStr != null then
firstStr
else if nonNulls == [ ] then
null
else
# The non-null elements have to be attribute sets representing partial trees
# We need to recurse into those
zipAttrsWith (name: _unionTrees) nonNulls;
elemAt trees stringIndex
else if withoutNull == [ ] then
# If all trees are null, then the resulting tree is also null
null
else
# The non-null elements have to be attribute sets representing partial trees
# We need to recurse into those
zipAttrsWith (name: _unionTrees) withoutNull;
/**
Computes the intersection of two filesets.

View File

@@ -203,11 +203,6 @@ lib.mapAttrs mkLicense (
fullName = " BitTorrent Open Source License v1.1";
};
blessing = {
spdxId = "blessing";
fullName = "SQLite Blessing";
};
boehmGC = {
spdxId = "Boehm-GC";
fullName = "Boehm-Demers-Weiser GC License";
@@ -1622,13 +1617,6 @@ lib.mapAttrs mkLicense (
fullName = "Universal Permissive License";
};
valveSDK = {
fullName = "Valve Corporation Steamworks SDK Access Agreement";
url = "https://partner.steamgames.com/documentation/sdk_access_agreement";
free = false;
redistributable = true;
};
vim = {
spdxId = "Vim";
fullName = "Vim License";

View File

@@ -151,24 +151,7 @@ let
);
# Derived meta-data
useLLVM =
final.isFreeBSD
|| final.isOpenBSD
|| final.isUefi
|| final.isMsvc
||
# because GCC does not support this platform yet
(with final; isWindows && isAarch64);
# Use the split GCC package set (`gccNGPackages`) instead of the
# monolithic `gcc`. No platform selects it yet; it is opt-in, set
# explicitly on a platform spec, so that the split set can be exercised
# before anything depends on it.
#
# I (@Ericson2314) plan on making obscure low-tier platforms (e.g.
# NetBSD) use it soon, so we can dogfood GCC NG and thereby iron out its
# bugs.
useGccNG = false;
useLLVM = final.isFreeBSD || final.isOpenBSD;
libc =
if final.isDarwin then
@@ -193,7 +176,9 @@ let
"uclibc"
else if final.isAndroid then
"bionic"
else if final.isLinux then
else if
final.isLinux # default
then
"glibc"
else if final.isFreeBSD then
"fblibc"
@@ -205,8 +190,6 @@ let
"avrlibc"
else if final.isGhcjs then
null
else if final.isUefi then
null
else if final.isNone then
"newlib"
# TODO(@Ericson2314) think more about other operating systems

View File

@@ -93,6 +93,7 @@ rec {
config = "aarch64-unknown-linux-android";
androidSdkVersion = "35";
androidNdkVersion = "27";
libc = "bionic";
useAndroidPrebuilt = false;
useLLVM = true;
};
@@ -168,18 +169,22 @@ rec {
riscv64-embedded = {
config = "riscv64-none-elf";
libc = "newlib";
};
riscv32-embedded = {
config = "riscv32-none-elf";
libc = "newlib";
};
mips64-embedded = {
config = "mips64-none-elf";
libc = "newlib";
};
mips-embedded = {
config = "mips-none-elf";
libc = "newlib";
};
# https://github.com/loongson/la-softdev-convention/blob/master/la-softdev-convention.adoc#10-operating-system-package-build-requirements
@@ -196,17 +201,17 @@ rec {
mmix = {
config = "mmix-unknown-mmixware";
# Not `isNone`: the OS here is `mmixware`, so the bare-metal default does
# not apply.
libc = "newlib";
};
rx-embedded = {
config = "rx-none-elf";
libc = "newlib";
};
msp430 = {
config = "msp430-elf";
libc = "newlib";
};
avr = {
@@ -215,10 +220,12 @@ rec {
vc4 = {
config = "vc4-elf";
libc = "newlib";
};
or1k = {
config = "or1k-elf";
libc = "newlib";
};
m68k = {
@@ -243,6 +250,7 @@ rec {
arm-embedded = {
config = "arm-none-eabi";
libc = "newlib";
};
arm-embedded-nano = {
config = "arm-none-eabi";
@@ -250,6 +258,7 @@ rec {
};
armhf-embedded = {
config = "arm-none-eabihf";
libc = "newlib";
# GCC8+ does not build without this
# (https://www.mail-archive.com/gcc-bugs@gcc.gnu.org/msg552339.html):
gcc = {
@@ -260,31 +269,38 @@ rec {
aarch64-embedded = {
config = "aarch64-none-elf";
libc = "newlib";
rust.rustcTarget = "aarch64-unknown-none";
};
aarch64be-embedded = {
config = "aarch64_be-none-elf";
libc = "newlib";
};
ppc-embedded = {
config = "powerpc-none-eabi";
libc = "newlib";
};
ppcle-embedded = {
config = "powerpcle-none-eabi";
libc = "newlib";
};
i686-embedded = {
config = "i686-elf";
libc = "newlib";
};
x86_64-embedded = {
config = "x86_64-elf";
libc = "newlib";
};
microblaze-embedded = {
config = "microblazeel-none-elf";
libc = "newlib";
};
#
@@ -322,10 +338,16 @@ rec {
x86_64-unknown-uefi = {
config = "x86_64-unknown-uefi";
libc = null;
useLLVM = true;
linker = "lld";
};
aarch64-unknown-uefi = {
config = "aarch64-unknown-uefi";
libc = null;
useLLVM = true;
linker = "lld";
};
#
@@ -360,11 +382,12 @@ rec {
};
# mingw-w64 with ucrt for Aarch64, default compiler (which is LLVM
# see ./default.nix).
# because GCC does not support this platform yet).
mingw-ucrt-aarch64 = {
config = "aarch64-w64-mingw32";
libc = "ucrt";
rust.rustcTarget = "aarch64-pc-windows-gnullvm";
useLLVM = true;
};
# mingw-64 back compat
@@ -377,10 +400,12 @@ rec {
# Target the MSVC ABI
x86_64-windows = {
config = "x86_64-pc-windows-msvc";
useLLVM = true;
};
aarch64-windows = {
config = "aarch64-pc-windows-msvc";
useLLVM = true;
};
x86_64-cygwin = {
@@ -391,10 +416,12 @@ rec {
aarch64-freebsd = {
config = "aarch64-unknown-freebsd";
useLLVM = true;
};
x86_64-freebsd = {
config = "x86_64-unknown-freebsd";
useLLVM = true;
};
x86_64-netbsd = {

View File

@@ -63,12 +63,6 @@
{
# keep-sorted start case=no numeric=no block=yes
"3mp3ri0r" = {
email = "christoforus@xendit.co";
github = "3mp3ri0r";
githubId = 3140815;
name = "Christoforus Surjoputro";
};
_0b11stan = {
name = "Tristan Auvinet Pinaudeau";
email = "tristan@tic.sh";
@@ -295,11 +289,6 @@
name = "6543";
keys = [ { fingerprint = "8722 B61D 7234 1082 553B 201C B8BE 6D61 0E61 C862"; } ];
};
_66HEX = {
name = "Marek Jóźwiak";
github = "66HEX";
githubId = 168720167;
};
_6AA4FD = {
email = "f6442954@gmail.com";
github = "6AA4FD";
@@ -627,11 +616,6 @@
{ fingerprint = "CE85 54F7 B9BC AC0D D648 5661 AB5F C04C 3C94 443F"; }
];
};
ad-si = {
name = "Adrian Sieber";
github = "ad-si";
githubId = 36796532;
};
ad030 = {
name = "Alex Dam";
github = "ad030";
@@ -891,12 +875,6 @@
githubId = 6055037;
name = "Alexander Hirner";
};
AhmedAmr = {
email = "ahmedamr24680@gmail.com";
github = "AhmedAmrNabil";
githubId = 43810060;
name = "Ahmed Amr";
};
ahoneybun = {
email = "aaronhoneycutt@proton.me";
github = "ahoneybun";
@@ -1450,11 +1428,6 @@
githubId = 30437811;
name = "Alex Andrews";
};
alikaansun = {
github = "alikaansun";
githubId = 77810345;
name = "Ali Kaan Sunnetcioglu";
};
alikindsys = {
email = "alice@blocovermelho.org";
github = "alikindsys";
@@ -2352,12 +2325,6 @@
githubId = 8049011;
name = "Arik Grahl";
};
arison = {
email = "arison@duck.com";
github = "ArisoN-ext";
githubId = 181835726;
name = "ArisoN";
};
ariutta = {
email = "anders.riutta@gmail.com";
github = "ariutta";
@@ -5188,11 +5155,6 @@
githubId = 1103294;
name = "Christopher Rosset";
};
christo-auer = {
name = "Christopher Auer";
github = "christo-auer";
githubId = 15138131;
};
christoph-heiss = {
email = "christoph@c8h4.io";
github = "christoph-heiss";
@@ -7161,12 +7123,6 @@
githubId = 15774340;
name = "Thomas Depierre";
};
dibenzepin = {
name = "Fumnanya";
email = "fmowete@outlook.com";
github = "dibenzepin";
githubId = 87488715;
};
DictXiong = {
email = "me@beardic.cn";
github = "DictXiong";
@@ -7608,6 +7564,12 @@
githubId = 81854406;
name = "Chew Cheng Hong";
};
drew-dirac = {
email = "drew@diracinc.com";
github = "drew-dirac";
githubId = 187309685;
name = "Drew Council";
};
drperceptron = {
github = "drperceptron";
githubId = 92106371;
@@ -7787,8 +7749,7 @@
matrix = "@dvdznf:gitter.im";
};
dvn0 = {
email = "devan@informatics.coop";
keys = [ { fingerprint = "E0F4 87C6 6298 7353 A7D0 E997 8203 BD5C 41D8 29DF"; } ];
email = "git@dvn.me";
github = "dvn0";
githubId = 10859387;
name = "Devan Carpenter";
@@ -8151,13 +8112,6 @@
githubId = 7494394;
name = "Karim Elatov";
};
eldios = {
email = "emanuele.lele.calo@gmail.com";
github = "eldios";
githubId = 483767;
name = "Emanuele 'Lele' Calo";
keys = [ { fingerprint = "AA6B C774 3F8F 9AD8 4BBA 15C7 2CCB F4B7 1EFF DD46"; } ];
};
eleanor = {
email = "dejan@proteansec.com";
github = "proteansec";
@@ -8804,12 +8758,6 @@
githubId = 330292;
name = "Evan Richter";
};
evanwporter = {
email = "evanwporter@gmail.com";
github = "evanwporter";
githubId = 115374841;
name = "Evan Porter";
};
evax = {
email = "nixos@evax.fr";
github = "evax";
@@ -10132,11 +10080,6 @@
name = "Kirill Samoylenkov";
keys = [ { fingerprint = "955B 97C5 78A3 DF03 D818 25EB 8E40 5DD2 CF84 CCE0"; } ];
};
gefla = {
name = "Gerd Flaig";
github = "gefla";
githubId = 56790;
};
genga898 = {
email = "genga898@gmail.com";
github = "genga898";
@@ -10183,12 +10126,7 @@
github = "gepbird";
githubId = 29818440;
name = "Gutyina Gergő";
matrix = "@gepbird:matrix.org";
keys = [
{ fingerprint = "hEzYpII9pUMfbhhr1LbUaLoGKGKX7UJQ1i9QU5SIssQ"; }
{ fingerprint = "MnieeyZ1Y02OB4PMrkM5xrk3ZXq3XTahcSocDyqzmFg"; }
{ fingerprint = "YfsHXlTvwc3HmLzQPi4g9nn4bKCj/mV/xgg9dxxxdnU"; }
{ fingerprint = "2NGbD6uF4OYtqjaYvN4foZFooT7XTAHODUaNxGVuJdk"; }
{ fingerprint = "RoAfvqa6w1l8Vdm3W60TDXurYwJ6h03VEGD+wDNGEwc"; }
{ fingerprint = "MP2UpIRtJpbFFqyucP431H/FPCfn58UhEUTro4lXtRs"; }
];
@@ -11335,6 +11273,11 @@
githubId = 58676303;
name = "hhydraa";
};
hibiday = {
name = "Katsumi Takeuchi";
github = "hibiday";
githubId = 137286929;
};
higebu = {
name = "Yuya Kusakabe";
email = "yuya.kusakabe@gmail.com";
@@ -12520,13 +12463,6 @@
githubId = 7558482;
name = "Jack Gerrits";
};
jackoe = {
name = "Jack Wines";
email = "nixos@winesj.com";
github = "jackoe";
githubId = 3060163;
matrix = "@noiobeforebedtime:winesj.com";
};
jackr = {
name = "Jack Rosenberg";
email = "nixos@jackr.eu";
@@ -13646,12 +13582,6 @@
githubId = 32305209;
name = "John Children";
};
johnhamelink = {
email = "me@johnhame.link";
github = "johnhamelink";
githubId = 101739;
name = "John Hamelink";
};
johnjohnstone = {
email = "jjohnstone@riseup.net";
github = "johnjohnstone";
@@ -16192,11 +16122,7 @@
};
liamthexpl0rer = {
name = "Liam";
matrix = "@liamthexpl0rer:l14mx.de";
keys = [
{ fingerprint = "3C0A 0FC8 E406 E602 50F3 FCFD 7633 7F2C A1CB 537D"; }
{ fingerprint = "CC53 895B 3CC7 7B29 AA46 55EF 6DF0 2F41 092A 9B30"; }
];
matrix = "@liamthexpl0rer:matrix.org";
github = "liamthexpl0rer";
githubId = 119797945;
};
@@ -17000,13 +16926,6 @@
}
];
};
lunkentuss = {
email = "peter.hansson17@gmail.com";
matrix = "@lunkentuss:matrix.org";
github = "lunkentuss";
githubId = 9850798;
name = "Peter Hansson";
};
LunNova = {
email = "nixpkgs-maintainer@lunnova.dev";
github = "LunNova";
@@ -17044,17 +16963,6 @@
matrix = "@lux:ontheblueplanet.com";
name = "Lux";
};
luytan = {
email = "luytan@khora.me";
github = "luytan";
githubId = 221864923;
name = "Luytan";
keys = [
{
fingerprint = "E7B7 215C 0DFB 3D8C 17EE 95E0 E0AD 187A 4F2B 41EF";
}
];
};
luz = {
email = "luz666@daum.net";
github = "Luz";
@@ -18949,12 +18857,6 @@
githubId = 54669781;
keys = [ { fingerprint = "293B 93D8 A471 059F 85D7 16A6 5BA9 2099 D9BE 2DAA"; } ];
};
mishushakov = {
email = "mish@e2b.dev";
github = "mishushakov";
githubId = 10400064;
name = "Mish Ushakov";
};
misilelab = {
name = "misilelab";
email = "misileminecord@gmail.com";
@@ -19321,13 +19223,6 @@
matrix = "@motiejus:jakstys.lt";
name = "Motiejus Jakštys";
};
Mowerick = {
email = "oliverhagenauer@gmail.com";
github = "Mowerick";
githubId = 102822250;
keys = [ { fingerprint = "6472 901A F0E7 F983 4893 042F 5F76 361C 2EF9 C95F"; } ];
name = "Oliver Hagenauer";
};
mpcsh = {
email = "m@mpc.sh";
github = "mpcsh";
@@ -20615,12 +20510,6 @@
githubId = 70602908;
github = "nikolaizombie1";
};
nikp123 = {
name = "nikp123";
email = "nikp123@e.email";
github = "nikp123";
githubId = 4696350;
};
nikstur = {
email = "nikstur@outlook.com";
name = "nikstur";
@@ -21889,12 +21778,6 @@
githubId = 4908217;
name = "Paho Lurie-Gregg";
};
paige = {
email = "paigely@tuta.io";
github = "ssalggnikool";
githubId = 235818692;
name = "paige";
};
pakhfn = {
email = "pakhfn@gmail.com";
github = "pakhfn";
@@ -22924,6 +22807,12 @@
githubId = 4201956;
name = "pongo1231";
};
poopsicles = {
name = "Fumnanya";
email = "fmowete@outlook.com";
github = "dibenzepin";
githubId = 87488715;
};
PopeRigby = {
name = "PopeRigby";
github = "poperigby";
@@ -23652,13 +23541,6 @@
github = "rachalaraj";
githubId = 124191100;
};
rachitvrma = {
name = "Rachit Kumar Verma";
email = "rachitverma1122+nixpkgs@gmail.com";
matrix = "@rachitvrma:matrix.org";
github = "rachitvrma";
githubId = 155641117;
};
RadxaYuntian = {
# This is the work account for @MakiseKurisu
name = "ZHANG Yuntian";
@@ -23967,12 +23849,6 @@
name = "Roland Conybeare";
keys = [ { fingerprint = "bw5Cr/4ul1C2UvxopphbZbFI1i5PCSnOmPID7mJ/Ogo"; } ];
};
rdk31 = {
email = "nixpkgs@rdk31.com";
github = "rdk31";
githubId = 16737959;
name = "rdk31";
};
rdnetto = {
email = "rdnetto@gmail.com";
github = "rdnetto";
@@ -23996,12 +23872,6 @@
{ fingerprint = "01D7 5486 3A6D 64EA AC77 0D26 FBF1 9A98 2CCE 0048"; }
];
};
recutita = {
name = "Katsumi Takeuchi";
email = "contact@recutita.com";
github = "recutita";
githubId = 137286929;
};
redfish64 = {
email = "engler@gmail.com";
github = "redfish64";
@@ -32012,12 +31882,6 @@
githubId = 3449926;
name = "David Costa";
};
ZariTen = {
email = "ZaritenProt@proton.me";
github = "ZariTen";
githubId = 48529745;
name = "ZariTen";
};
zatm8 = {
email = "maxis1191@gmail.com";
github = "mourogurt";

View File

@@ -23,7 +23,6 @@ digestif,,,,,5.3,
dkjson,,,,,,
enet,,,,,,ulysseszhan
etlua,,,,,,ulysseszhan
fallo,,,,,,mrcjkb
fennel,,,,,,misterio77
fidget.nvim,,,,,5.1,mrcjkb
fifo,,,,,,
1 name rockspec ref server version luaversion maintainers
23 dkjson
24 enet ulysseszhan
25 etlua ulysseszhan
fallo mrcjkb
26 fennel misterio77
27 fidget.nvim 5.1 mrcjkb
28 fifo

View File

@@ -10,9 +10,6 @@ stdenv.mkDerivation {
pname = "nixpkgs-lint";
version = "1";
__structuredAttrs = true;
strictDeps = true;
nativeBuildInputs = [ makeWrapper ];
buildInputs = [
perl
@@ -23,20 +20,16 @@ stdenv.mkDerivation {
dontBuild = true;
installPhase = ''
runHook preInstall
mkdir -p $out/bin
cp ${./nixpkgs-lint.pl} $out/bin/nixpkgs-lint
# make the built version hermetic
substituteInPlace $out/bin/nixpkgs-lint \
--replace-fail "#! /usr/bin/env nix-shell" "#! ${lib.getExe perl}"
wrapProgram $out/bin/nixpkgs-lint --set PERL5LIB $PERL5LIB
runHook postInstall
'';
meta = {
description = "Utility for Nixpkgs contributors to check Nixpkgs for common errors";
description = "A utility for Nixpkgs contributors to check Nixpkgs for common errors";
mainProgram = "nixpkgs-lint";
platforms = lib.platforms.unix;
};

View File

@@ -402,7 +402,6 @@ with lib.maintainers;
GaetanLepage
natsukium
thomasjm
haansn08
];
scope = "Maintain Jupyter and related packages.";
shortName = "Jupyter";
@@ -711,6 +710,7 @@ with lib.maintainers;
sage = {
members = [
timokau
raskin
collares
];

View File

@@ -395,7 +395,7 @@ have a predefined type and string generator already declared under
`mkRaw pythonCode`
: Outputs the given string as raw Python code. Note that the final result will be stripped of any comments.
: Outputs the given string as raw Python code
`_imports`

View File

@@ -1,57 +0,0 @@
# State revision {#sec-state-revision}
NixOS includes a {option}`system.stateVersion` option, used by some modules for a
variety of reasons related to non-backward-compatible changes to software or
the module itself.
Module authors are discouraged from adding new uses of
{option}`system.stateVersion` to their module.
However, when the alternatives are impractical, modules that wish to consume
{option}`system.stateVersion` should instead define their own `stateRevision`
option using `utils.mkStateRevisionOption`.
There should be no uses of `config.system.stateVersion` directly in the module.
(Note the name difference: the {option}`system.stateVersion` option, with a V,
takes a value that looks like "YY.MM".
A `stateRevision` option, with an R, takes a non-negative integer value.)
Modules should also add the value of their `stateRevision` option to
`system.moduleStateRevisions."your.module.stateRevision"`, when the module is
enabled.
This is a purely informative option that exists to help describe the effects of
changing {option}`system.stateVersion`.
Example:
```nix
{
lib,
config,
utils,
...
}:
let
cfg = config.services.whatever;
in
{
options.services.whatever = {
enable = lib.mkEnableOption "whatever, a service that does whatever";
stateRevision = utils.mkStateRevisionOption {
descriptionName = "the whatever service";
migrations = {
"26.05" = "Rename `/var/lib/old_name` to `/var/lib/new_name`.";
};
};
};
config = lib.mkIf cfg.enable {
systemd.services.whatever = {
# ...
serviceConfig.StateDirectory = if cfg.stateRevision < 1 then "old_name" else "new_name";
};
# Important: this is inside the `lib.mkIf cfg.enable`
system.moduleStateRevisions."services.whatever.stateRevision" = cfg.stateRevision;
};
}
```

View File

@@ -220,5 +220,4 @@ importing-modules.section.md
replace-modules.section.md
freeform-modules.section.md
settings-options.section.md
state-revision.section.md
```

View File

@@ -41,22 +41,8 @@ supported stable release.
When you first install NixOS, you're automatically subscribed to the
NixOS channel that corresponds to your installation source. For
instance, if you installed from a 26.05 ISO, you will be subscribed to
the `nixos-26.05` channel.
Commands below are prefixed with `#` and have to be run as root in a
login shell:
```ShellSession
$ sudo -i
```
Without `sudo`:
```ShellSession
$ su -
```
To see which NixOS channel you're subscribed to, run:
the `nixos-26.05` channel. To see which NixOS channel you're subscribed
to, run the following as root:
```ShellSession
# nix-channel --list | grep nixos
@@ -98,15 +84,9 @@ by running
which is equivalent to the more verbose `nix-channel --update nixos; nixos-rebuild switch`.
::: {.note}
Channels are set per user. `nix-channel` reads and writes
`$HOME/.nix-channels`, so it acts on the channels of whoever owns the
current `$HOME`. A login shell sets `$HOME` to `/root`, which is why the
commands above act on root's channels — the ones
`/etc/nixos/configuration.nix` uses.
Plain `sudo` and `su` keep your own `$HOME`. `nix-channel --list` then
lists your own channels, and prints nothing when you have none.
`nix-channel --add` adds the channel for your user alone.
Channels are set per user. This means that running `nix-channel --add`
as a non root user (or without sudo) will not affect
configuration in `/etc/nixos/configuration.nix`
:::
::: {.warning}

View File

@@ -7,5 +7,5 @@ Additional information regarding the Nix package manager and the Nixpkgs project
If you encounter problems, please report them on the [`Discourse`](https://discourse.nixos.org), the [Matrix room](https://matrix.to/#/%23nix:nixos.org), or on the [`#nixos` channel on Libera.Chat](irc://irc.libera.chat/#nixos). Alternatively, consider [contributing to this manual](#chap-contributing). Bugs should be reported in [NixOS GitHub issue tracker](https://github.com/NixOS/nixpkgs/issues).
::: {.note}
Commands prefixed with `#` have to be run as root.
Commands prefixed with `#` have to be run as root, either requiring to login as root user or temporarily switching to it using `sudo` for example.
:::

View File

@@ -253,9 +253,6 @@
"sec-override-nixos-test": [
"index.html#sec-override-nixos-test"
],
"sec-state-revision": [
"index.html#sec-state-revision"
],
"sec-wireless-declarative": [
"index.html#sec-wireless-declarative"
],

View File

@@ -28,14 +28,8 @@
firewall, is available through
[services.portmaster](#opt-services.portmaster.enable).
- [btrfs-heatmap](https://github.com/knorrie/btrfs-heatmap), setcap wrapper for `btrfs-heatmap` package, a visualizer of how a btrfs filesystem is using the underlying disk space of the block devices. Available as [programs.btrfs-heatmap](#opt-programs.btrfs-heatmap.enable)
- [compsize](https://github.com/kilobyte/compsize), setcap wrapper for `compsize` package, a cli utility to to inspect compression type/ratio on BTRFS filesystems. Available as [programs.compsize](#opt-programs.compsize.enable)
- [tranquil](https://tangled.org/tranquil.farm/tranquil-pds) is an ATProto PDS (personal data server) implementation in Rust. A featureful, spec conscious and community driven alternative to the Bluesky reference implementation PDS. Available as [services.tranquil-pds](#opt-services.tranquil-pds.enable).
- [Cardwire](https://github.com/OpenGamingCollective/cardwire), a GPU manager for Linux that uses eBPF+LSM hooks to control GPUs. Available as [services.cardwired](#opt-services.cardwired.enable).
- [Moonlight Qt](https://moonlight-stream.org/), a client for playing your PC games on almost any device. Available as [programs.moonlight-qt](#opt-programs.moonlight-qt.enable).
- [RomM](https://romm.app/), a self-hosted ROM manager and player. Available as [services.romm](#opt-services.romm.enable).
@@ -64,14 +58,10 @@
- [Lix TOML remote builders](https://docs.lix.systems/manual/lix/stable/advanced-topics/distributed-builds.html#using-a-toml-configuration), remote builder configuration using lix's TOML format. Available as [lix.buildMachines](#opt-lix.buildMachines). Note: incompatible with `nix.buildMachines`.
- [Forgejo Runner](https://forgejo.org/docs/latest/admin/actions/), a daemon for Forgejo Actions. Available as [services.forgejo-runner](#opt-services.forgejo-runner.instances).
- [Koito](https://koito.io/), a modern, themeable scrobbler that you can use with any program that scrobbles to a custom ListenBrainz URL. Available as [services.koito](#opt-services.koito.enable).
- [Zapret2](https://github.com/bol-van/zapret2), an extensible DPI bypass program. Available as [services.zapret2](#opt-services.zapret2.enable).
- [Solaar](https://github.com/pwr-Solaar/Solaar), a program to control logitech devices.
- [FlapAlerted](https://github.com/Kioubit/FlapAlerted), detects BGP flapping events and provides statistics based on BGP update messages. Available as [services.flap-alerted](#opt-services.flap-alerted.enable).
- [gocron](https://github.com/flohoss/gocron), a task scheduler with web interface. Available as [services.gocron](#opt-services.gocron.enable).
@@ -82,8 +72,6 @@
- [Matrix Authentication Service](https://github.com/element-hq/matrix-authentication-service) is an OAuth2.0 and OpenID Connect provider for Matrix homeservers (such as Synapse). It replaces standard password authentication with modern OpenID Connect flows, and can delegate authentication to upstream OIDC providers. Available as [services.matrix-authentication-service](#opt-services.matrix-authentication-service.enable).
- [Krill](https://nlnetlabs.nl/projects/krill/about), RPKI CA and Publication Server written in Rust. Available as [services.krill](#opt-services.krill.enable).
- [stash-clipboard](https://github.com/NotAShelf/stash), a Wayland clipboard "manager" with fast persistent history and multi-media support. Available as [services.stash-clipboard](#opt-services.stash-clipboard.enable).
- [OO7](https://github.com/linux-credentials/oo7) is a desktop-agnostic Secret Service provider. Available as [services.oo7](#opt-services.oo7.enable)
@@ -96,10 +84,6 @@
- [Entropy](https://github.com/ergohaven/entropy), a configurator for programmable keyboards and input devices running Vial-QMK/RMK firmware. Available as [programs.entropy](#opt-programs.entropy.enable).
- [Kvrocks](https://kvrocks.apache.org/), a distributed key value NoSQL database compatible with the Redis protocol. Available as [services.kvrocks](#opt-services.kvrocks.enable).
- [kvrocks_exporter](https://github.com/RocksLabs/kvrocks_exporter), a Prometheus exporter for Kvrocks metrics. Available as [services.prometheus.exporters.kvrocks](#opt-services.prometheus.exporters.kvrocks.enable).
## Backward Incompatibilities {#sec-release-26.11-incompatibilities}
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
@@ -134,8 +118,6 @@
- Rustical migrates from `settings.http.host` and `settings.http.port` to `settings.http.bind` to support UNIX domain sockets as well as TCP sockets in one setting.
- The `jetty_11` package has been removed as it reached end of life. Use `jetty_12` instead.
- The Mullvad VPN service now has a separate toggle to enable the Mullvad VPN graphical user interface. If you have previously used Mullvad on a desktop by setting `services.mullvad-vpn.package` to `pkgs.mullvad-vpn`, you should now **unset that option**, and enable `services.mullvad-vpn.gui.enable`. The VPN will not work if `services.mullvad-vpn.package` is set to `pkgs.mullvad-vpn`, as `pkgs.mullvad-vpn` no longer contains the Mullvad Daemon; please ensure that `services.mullvad-vpn.package` is set to `pkgs.mullvad`, regardless if you plan to enable the graphical user interface or not.
- A number of options for `services.llama-cpp` have been removed in favor of the structured [](#opt-services.llama-cpp.settings) option, attributes from which are used as arguments to `llama-server` executable, you can see all available options by running `llama-server --help`. Configuring model presets using Nix attribute set via `services.llama-cpp.modelsPreset` is no longer supported, please use `services.llama-cpp.settings.models-preset` with a path to an INI file containing desired options.
@@ -162,12 +144,8 @@
- String values passed to `services.phpfpm.settings`, `services.phpfpm.pools.<name>.phpEnv`, and `services.phpfpm.pools.<name>.settings` are now properly quoted and escaped, except for the `${}` syntax that is left as-is. If you are manually escaping these values, please adjust accordingly.
- GitLab has been updated from 18.x to 19.x and requires PostgreSQL >= 17, as stated in the [documentation](https://docs.gitlab.com/19.1/install/requirements/#postgresql). Check the [upgrade guide](#module-services-postgres-upgrading) in the NixOS manual on how to upgrade your PostgreSQL installation.
- `services.gitlab.registry` has been modified so that the GitLab container registry runs in the `gitlab-container-registry` system user. This behavior can be modified with the `services.gitlab.registry.user` option.
- `fail2ban` has been updated to 1.1.1, which has a few breaking changes compared to 1.1.0 ([changelog](https://github.com/fail2ban/fail2ban/blob/1.1.1/ChangeLog))
- `systemd.user.extraConfig` has been removed in favor of the structured [](#opt-systemd.user.settings.Manager) option. Use `systemd.user.settings.Manager` to set any `systemd-user.conf(5)` option directly. For example, replace `systemd.user.extraConfig = "DefaultTimeoutStartSec=60";` with `systemd.user.settings.Manager.DefaultTimeoutStartSec = 60;`.
- `matrix-appservice-discord` was removed from nixpkgs along with its NixOS module (`services.matrix-appservice-discord`) as it is no longer actively maintained upstream. Use the actively-maintained puppeting bridge [`mautrix-discord`](#opt-services.mautrix-discord.enable) instead.
@@ -201,8 +179,6 @@
- `temporal` has been updated to the 1.31 release line. Always consult the [upstream upgrade
notes](https://docs.temporal.io/self-hosted-guide/upgrade-server) before upgrading between versions.
- The Xen Project Hypervisor has been [updated to version 4.22](https://wiki.xenproject.org/wiki/Xen_Project_4.22_Release_Notes), after [version 4.21](https://wiki.xenproject.org/wiki/Xen_Project_4.21_Release_Notes) was skipped in 26.05. The module now has a separate option to customise the OCaml-based Xen Store Daemon package, `virtualisation.xen.store.package`.
- The `shell_interact()` function on interactive runs of NixOS VM tests has been deprecated. Use the SSH backdoor instead.
- NixOS VM tests now prefer to express durations and timeouts as `datetime.timedelta` values instead of bare numbers. Methods such as `machine.wait_until_succeeds`, `machine.sleep`, `retry`, and `polling_condition` now accept a `timedelta` (e.g., `machine.wait_for_unit("sshd.service", timeout=datetime.timedelta(minutes=1))`). Passing an `int`/`float` as seconds still works but now emits a deprecation warning. Argument names that explicitly defined units were preserved but have had `timedelta` equivalents introduced (`timeout_seconds` → `timeout`, `secs` → `duration`, `seconds_interval` → `interval`).
@@ -237,8 +213,6 @@
- `boot.supportedFilesystems.ntfs` installs `ntfsprogs-plus` instead of `ntfs3g` on kernel version 7.1 and later, unless `boot.supportedFilesystems.ntfs-3g` is explicitly enabled.
- `services.i2pd` has been refactored to take [RFC42](https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md)-compliant `settings`. In order to migrate, you will need to move existing config under `settings` and rename them in accordance with the [upstream config format](https://docs.i2pd.website/en/latest/user-guide/configuration/#available-options). In addition, `inTunnels` and `outTunnels` needs to be renamed to `serverTunnels` and `clientTunnels` respectively.
- The `programs.fuse` module, which provides the `fusermount3` executable and the `/etc/fuse.conf` config file, is now opt-in. The obligation to enable it has been shifted to its various consumers (e.g. gvfs, flatpak, appimage, sshfs). This can break fuse consumers at runtime, that don't explicitly declare that dependency with a module, e.g the mounting functionality in various backup tools (borg, restic, rclone, ...).
- `services.plausible` can now again seed an initial admin user declaratively via [`services.plausible.adminUser.email`](#opt-services.plausible.adminUser.email).
@@ -247,8 +221,6 @@
- `services.gitlab.registry` now uses PostgreSQL as database storage for new installations and supports old installations that use the filesystem as metadata storage. It creates the required PostgreSQL database and user. Users can manually migrate their filesystem based metadata storage. See [GitLab Container Registry Migration to database metadata store](#module-services-gitlab-registry-database-migration).
- Enabling [`services.userborn`](#opt-services.userborn.enable) on a system that was previously managed by the default `update-users-groups.pl` script now imports the legacy state from `/var/lib/nixos/` on the first switch. Locked stub entries are added to `/etc/passwd` and `/etc/group` for every name recorded in `uid-map`/`gid-map` that no longer has a live entry, so a previously-used UID/GID cannot be reassigned to a different user. If the import fails, userborn does not start and the user database is left untouched. Inspect `journalctl -u userborn-import-legacy.service`, fix or remove the legacy state, and switch again. The import can be skipped entirely with [`services.userborn.importLegacyState`](#opt-services.userborn.importLegacyState)` = false`.
- The `newuidmap` and `newgidmap` security wrappers are now installed with `cap_setuid`/`cap_setgid` file capabilities instead of the setuid-root bit, matching shadow's `--with-fcaps` install mode and other major distributions. Rootless containers (podman, docker-rootless, unprivileged user namespaces) are unaffected. The only behavioural change is that mapping host uid 0 via `/etc/subuid` (which NixOS never configures by default) additionally requires `cap_setfcap`; users who explicitly grant uid 0 in a subuid range can restore the previous behaviour with `security.wrappers.newuidmap.capabilities = lib.mkForce "cap_setuid,cap_setfcap+ep";`.
- The `authelia` module now uses systemd's `LoadCredential` to load all files defined in `secrets`. As such, these files no longer need to be readable by the authelia user and group: they can for example be set to be only readable by the root user.
@@ -265,5 +237,3 @@
- `trilium-desktop` and `trilium-server` have been updated to 0.104.0. This release includes security hardening fixes that may break functionality. [See upstream release note for details](https://github.com/TriliumNext/Trilium/releases/tag/v0.104.0).
- `nix` now supports running in "daemonless" mode by setting `nix.daemon.enable = false`. Under this mode all store operations must go through the [local store type](https://nix.dev/manual/nix/latest/store/types/local-store), which typically requires root permissions.
- [Hister](https://github.com/asciimoo/hister), a web history service offering blazing fast, content-based search across visited websites. Available as [services.hister](#opt-services.hister.enable).

View File

@@ -6,7 +6,7 @@
xorriso,
syslinux,
libossp_uuid,
squashfs-tools,
squashfsTools,
# The file name of the resulting ISO image.
isoName ? "cd.iso",

View File

@@ -1,7 +1,7 @@
{
lib,
stdenv,
squashfs-tools,
squashfsTools,
closureInfo,
fileName ? "squashfs",
@@ -31,7 +31,7 @@ stdenv.mkDerivation {
# to the closure that was used to build it
unsafeDiscardReferences.out = true;
nativeBuildInputs = [ squashfs-tools ];
nativeBuildInputs = [ squashfsTools ];
buildCommand = ''
closureInfo=${closureInfo { rootPaths = storeContents; }}

View File

@@ -60,7 +60,7 @@ buildPythonApplication {
util-linux
vde2
]
++ lib.optionals stdenv.hostPlatform.isLinux [
++ lib.optionals stdenv.isLinux [
vhost-device-vsock
]
++ lib.optionals enableNspawn [

View File

@@ -8,12 +8,12 @@ testModuleArgs@{
}:
let
inherit (lib) mkOption types;
inherit (types) either lines functionTo;
inherit (types) either str functionTo;
in
{
options = {
testScript = mkOption {
type = either lines (functionTo lines);
type = either str (functionTo str);
apply =
v:
if lib.isFunction v then
@@ -27,7 +27,7 @@ in
'';
};
testScriptString = mkOption {
type = lines;
type = str;
readOnly = true;
internal = true;
};

View File

@@ -6,10 +6,8 @@
let
inherit (lib)
all
any
attrNames
concatImapStringsSep
concatMapStringsSep
concatStringsSep
elem
@@ -29,11 +27,8 @@ let
isList
isPath
isString
length
listToAttrs
literalMD
mapAttrs
mkOption
nameValuePair
optionalString
removePrefix
@@ -41,10 +36,8 @@ let
splitString
stringToCharacters
types
versionOlder
;
inherit (lib.lists) findFirstIndex;
inherit (lib.strings) toJSON escapeC;
in
@@ -611,122 +604,6 @@ let
lib.listToAttrs
];
};
/**
Creates a per-module `stateRevision` option that takes an int value, with a
default that is derived from `system.stateVersion`.
# Inputs
`descriptionName`
: A human-friendly name for your module, used for the description of the
created option.
`migrations`
: Attribute set that maps from values of `system.stateVersion`
(representing the breakpoints at which the default value of this option
will change) to Markdown instructions to users for manually migrating
their data to this breakpoint. The migration instructions will be
included in the NixOS documentation for this option. (These instructions
must only contain Markdown inlines, because they will be rendered as
items in an ordered list. In particular, nested lists will not render
correctly.)
`migrations` will also be exposed as an attribute on the result.
# Examples
:::{.example}
## `lib.options.mkStateRevisionOption` usage example
```nix
exampleModule =
{ lib, config, utils, ... }:
{
options.services.whatever = {
stateRevision = utils.mkStateRevisionOption {
descriptionName = "the whatever service";
migrations = {
"26.05" = "Rename `/var/lib/old_name` to `/var/lib/new_name`.";
"26.11" = "Run the `upgrade_whatever` utility.";
};
};
};
};
}
(pkgs.nixos [
exampleModule
{ system.stateVersion = "25.11"; }
]).config.services.whatever.stateRevision # => 0
(pkgs.nixos [
exampleModule
{ system.stateVersion = "26.05"; }
]).config.services.whatever.stateRevision # => 1
(pkgs.nixos [
exampleModule
{ system.stateVersion = "27.05"; }
]).config.services.whatever.stateRevision # => 2
```
:::
Modules should use this function when they change how data managed by the
module is persisted on the system between NixOS releases.
The default value of the option will be the number of attributes in the
`migrations` parameter with name less than or equal to the value of
`system.stateVersion`.
When using this function, don't forget to add the option's value to
`system.moduleStateRevisions."your.module.stateRevision"` when your module is
enabled.
*/
mkStateRevisionOption =
{
descriptionName,
migrations,
}:
let
versions = attrNames migrations;
maxVal = length versions;
in
assert all (v: builtins.match "[0-9]{2}\\.[0-9]{2}" v != null) versions;
mkOption {
type = types.ints.between 0 maxVal;
description = ''
This option versions the format of state persisted by
${descriptionName}. Its default value depends on the value of
{option}`system.stateVersion`.
Users who wish to increment this option will need to take manual
migration steps to preserve their data. **If you perform these
migrations, rolling back to an older generation will require also
reversing the migrations to the state expected by that generation.**
The migrations needed to advance to each value of this option are as
follows (perform all instructions after the row for the current
`stateRevision`, up to and including the row for the new
`stateRevision`):
0. (none)
${concatImapStringsSep "\n" (
v: sv: "${toString v}. ${replaceStrings [ "\n" ] [ " " ] migrations.${sv}}"
) versions}
Note that you do **not** need to change {option}`system.stateVersion`
in order to update this option. {option}`system.stateVersion` only
determines the default value of this option. Most users should not
change {option}`system.stateVersion` at all.
'';
default = findFirstIndex (versionOlder config.system.stateVersion) maxVal versions;
defaultText = literalMD ''
If {option}`system.stateVersion` is:
${concatImapStringsSep "\n" (v: sv: "* &lt;${sv}: ${toString (v - 1)}") versions}
* otherwise: ${toString maxVal}
'';
}
// {
inherit migrations;
};
};
in
utils

View File

@@ -109,10 +109,6 @@ in
"pata_qdi"
"pata_winbond"
]
++ lib.optionals (lib.versionAtLeast config.boot.kernelPackages.kernel.version "7.2") [
# xhci-pci defers AMD 800-series chipset controllers to this driver.
"xhci_pci_prom21"
]
++ lib.optionals platform.isx86 [ "vmw_balloon" ]
++ lib.optionals (pkgs.stdenv.hostPlatform.isi686 || pkgs.stdenv.hostPlatform.isx86_64) [
"vmw_vmci"

View File

@@ -19,8 +19,8 @@ in
[ "hardware" "logitech" "wireless" "enable" ]
)
(lib.mkRenamedOptionModule
[ "hardware" "logitech" "enableGraphical" ]
[ "hardware" "logitech" "wireless" "enableGraphical" ]
[ "programs" "solaar" "enable" ]
)
];
@@ -56,11 +56,20 @@ in
wireless = {
enable = lib.mkEnableOption "support for Logitech Wireless Devices";
enableGraphical = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable graphical support applications.";
};
};
};
config = lib.mkIf (cfg.wireless.enable || cfg.lcd.enable) {
environment.systemPackages = lib.optional cfg.wireless.enable pkgs.ltunify;
environment.systemPackages =
[ ]
++ lib.optional cfg.wireless.enable pkgs.ltunify
++ lib.optional cfg.wireless.enableGraphical pkgs.solaar;
services.udev = {
# ltunifi and solaar both provide udev rules but the most up-to-date have been split

View File

@@ -482,7 +482,7 @@ in
combineIcdPkgs =
icd: pkgs:
pkgs.symlinkJoin {
name = "nvidia-egl-external-platforms${lib.optionalString pkgs.stdenv.hostPlatform.is32bit "-x32"}";
name = "nvidia-egl-external-platforms${lib.optionalString pkgs.stdenv.is32bit "-x32"}";
paths = lib.attrVals icd pkgs;
# Remediate reversed priorities in pre-595 drivers,
# https://github.com/NixOS/nixpkgs/pull/497342#issuecomment-4034876793

View File

@@ -17,7 +17,7 @@
dosfstools,
mtools,
e2fsprogs,
squashfs-tools,
squashfsTools,
erofs-utils,
btrfs-progs,
xfsprogs,
@@ -104,7 +104,7 @@ let
mtools
];
"ext4" = [ e2fsprogs.bin ];
"squashfs" = [ squashfs-tools ];
"squashfs" = [ squashfsTools ];
"erofs" = [ erofs-utils ];
"btrfs" = [ btrfs-progs ];
"xfs" = [ xfsprogs ];

View File

@@ -400,7 +400,7 @@ in
# Figure out device names for the boot device and root filesystem.
rootPart=$(${lib.getExe' pkgs.util-linux "findmnt"} -n -o SOURCE /)
bootDevice=$(${lib.getExe' pkgs.util-linux "lsblk"} -npo PKNAME $rootPart)
partNum=$(${lib.getExe' pkgs.util-linux "lsblk"} -npo PARTN $rootPart)
partNum=$(${lib.getExe' pkgs.util-linux "lsblk"} -npo MAJ:MIN $rootPart | ${lib.getExe pkgs.gawk} -F: '{print $2}')
# Resize the root partition and the filesystem to fit the disk
echo ",+," | ${lib.getExe' pkgs.util-linux "sfdisk"} -N$partNum --no-reread $bootDevice

View File

@@ -206,10 +206,10 @@ sub pciCheck {
$device eq "0x4222" || $device eq "0x4227");
# Intel NPU driver
# list taken from linux(v7.1): drivers/accel/ivpu/ivpu_drv.h
# list taken from linux(v6.18): drivers/accel/ivpu/ivpu_drv.h
if ($vendor eq "0x8086" &&
($device eq "0x7d1d" || $device eq "0xad1d" || $device eq "0x643e" ||
$device eq "0xb03e" || $device eq "0xfd3e" || $device eq "0xd71d"))
($device eq "0xfd3e" || $device eq "0x7d1d" || $device eq "0xad1d" ||
$device eq "0x643e" || $device eq "0xb03e"))
{
push @attrs, "hardware.cpu.intel.npu.enable = true;";
}

View File

@@ -194,7 +194,7 @@ in
redmine = 147;
#seeks = 148; # removed 2020-06-21
prosody = 149;
# i2pd = 150; # dynamically allocated as of 2026-08-07
i2pd = 150;
systemd-coredump = 151;
systemd-network = 152;
systemd-resolve = 153;
@@ -534,7 +534,7 @@ in
redmine = 147;
#seeks = 148; # removed 2020-06-21
prosody = 149;
# i2pd = 150; # dynamically allocated as of 2026-08-07
i2pd = 150;
systemd-network = 152;
systemd-resolve = 153;
systemd-timesync = 154;

View File

@@ -254,30 +254,6 @@ in
'';
};
moduleStateRevisions = mkOption {
type =
let
baseType = types.attrsOf types.ints.unsigned;
isStateRevisionOption = x: lib.isOption x && x ? migrations;
in
types.addCheck baseType (
attrs:
builtins.all (
attrPath: isStateRevisionOption (lib.attrByPath (lib.splitString "." attrPath) null options)
) (builtins.attrNames attrs)
)
// {
description = "${baseType.description}, in which every attribute name is the path to an option created with mkStateRevisionOption";
};
default = { };
internal = true;
description = ''
NixOS modules should set attributes on this option. Users should leave
it alone. Future tooling may use it to determine the consequences of
updating {option}`system.stateVersion`.
'';
};
configurationRevision = mkOption {
type = types.nullOr types.str;
default = null;

View File

@@ -185,7 +185,6 @@
./programs/bcc.nix
./programs/benchexec.nix
./programs/browserpass.nix
./programs/btrfs-heatmap.nix
./programs/calls.nix
./programs/captive-browser.nix
./programs/ccache.nix
@@ -197,7 +196,6 @@
./programs/cnping.nix
./programs/comma.nix
./programs/command-not-found/command-not-found.nix
./programs/compsize.nix
./programs/coolercontrol.nix
./programs/corefreq.nix
./programs/cpu-energy-meter.nix
@@ -328,7 +326,6 @@
./programs/skim.nix
./programs/slock.nix
./programs/sniffnet.nix
./programs/solaar.nix
./programs/soundmodem.nix
./programs/ssh.nix
./programs/starship.nix
@@ -350,7 +347,6 @@
./programs/tsm-client.nix
./programs/turbovnc.nix
./programs/udevil.nix
./programs/upki.nix
./programs/usbtop.nix
./programs/vim.nix
./programs/virt-manager.nix
@@ -523,7 +519,6 @@
./services/continuous-integration/buildbot/master.nix
./services/continuous-integration/buildbot/worker.nix
./services/continuous-integration/buildkite-agents.nix
./services/continuous-integration/forgejo-runner.nix
./services/continuous-integration/gitea-actions-runner.nix
./services/continuous-integration/github-runners.nix
./services/continuous-integration/gitlab-runner/runner.nix
@@ -553,7 +548,6 @@
./services/databases/hbase-standalone.nix
./services/databases/influxdb2.nix
./services/databases/influxdb.nix
./services/databases/kvrocks.nix
./services/databases/lldap.nix
./services/databases/memcached.nix
./services/databases/monetdb.nix
@@ -685,7 +679,6 @@
./services/hardware/bolt.nix
./services/hardware/brltty.nix
./services/hardware/buffyboard.nix
./services/hardware/cardwired.nix
./services/hardware/ddccontrol.nix
./services/hardware/deepcool-digital-linux.nix
./services/hardware/dell-bios-fan-control.nix
@@ -1295,7 +1288,6 @@
./services/networking/knot-resolver.nix
./services/networking/knot.nix
./services/networking/kresd.nix
./services/networking/krill.nix
./services/networking/lambdabot.nix
./services/networking/legit.nix
./services/networking/libreswan.nix
@@ -1719,7 +1711,6 @@
./services/web-apps/haven.nix
./services/web-apps/healthchecks.nix
./services/web-apps/hedgedoc.nix
./services/web-apps/hister.nix
./services/web-apps/hledger-web.nix
./services/web-apps/homebox.nix
./services/web-apps/homer.nix

View File

@@ -1,32 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
meta.maintainers = with lib.maintainers; [ sandarukasa ];
options = {
programs.btrfs-heatmap = {
enable = lib.mkEnableOption "btrfs-heatmap + setcap wrapper";
package = lib.mkPackageOption pkgs "btrfs-heatmap" { };
};
};
config =
let
cfg = config.programs.btrfs-heatmap;
in
lib.mkIf cfg.enable {
# for the man page
environment.systemPackages = [ cfg.package ];
security.wrappers.btrfs-heatmap = {
owner = config.users.users.root.name;
group = config.users.users.root.group;
capabilities = "cap_sys_admin+p";
source = lib.getExe cfg.package;
};
};
}

View File

@@ -85,32 +85,7 @@ in
extraOpts = lib.mkOption {
type = lib.types.attrs;
description = ''
Extra chromium policy options. These settings are locked and the user cannot change them in the browser later.
A list of available policies
can be found in the Chrome Enterprise documentation:
<https://cloud.google.com/docs/chrome-enterprise/policies/>
Make sure the selected policy is supported on Linux and your browser version.
'';
default = { };
example = lib.literalExpression ''
{
"BrowserSignin" = 0;
"SyncDisabled" = true;
"PasswordManagerEnabled" = false;
"SpellcheckEnabled" = true;
"SpellcheckLanguage" = [
"de"
"en-US"
];
}
'';
};
extraOptsRecommended = lib.mkOption {
type = lib.types.attrs;
description = ''
Extra chromium policy options in recommended. These are default settings. The user can change them in the browser later if they want to.
A list of available policies
Extra chromium policy options. A list of available policies
can be found in the Chrome Enterprise documentation:
<https://cloud.google.com/docs/chrome-enterprise/policies/>
Make sure the selected policy is supported on Linux and your browser version.
@@ -166,9 +141,6 @@ in
"chromium/policies/managed/extra.json" = lib.mkIf (cfg.extraOpts != { }) {
text = builtins.toJSON cfg.extraOpts;
};
"chromium/policies/recommended/extra.json" = lib.mkIf (cfg.extraOptsRecommended != { }) {
text = builtins.toJSON cfg.extraOptsRecommended;
};
"chromium/initial_preferences" = lib.mkIf (cfg.initialPrefs != { }) {
text = builtins.toJSON cfg.initialPrefs;
};
@@ -184,9 +156,6 @@ in
"opt/chrome/policies/managed/extra.json" = lib.mkIf (cfg.extraOpts != { }) {
text = builtins.toJSON cfg.extraOpts;
};
"opt/chrome/policies/recommended/extra.json" = lib.mkIf (cfg.extraOptsRecommended != { }) {
text = builtins.toJSON cfg.extraOptsRecommended;
};
# for brave
"brave/policies/managed/default.json" = lib.mkIf (defaultProfile != { }) {
text = builtins.toJSON defaultProfile;
@@ -194,9 +163,6 @@ in
"brave/policies/managed/extra.json" = lib.mkIf (cfg.extraOpts != { }) {
text = builtins.toJSON cfg.extraOpts;
};
"brave/policies/recommended/extra.json" = lib.mkIf (cfg.extraOptsRecommended != { }) {
text = builtins.toJSON cfg.extraOptsRecommended;
};
};
};
}

View File

@@ -1,32 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
meta.maintainers = with lib.maintainers; [ sandarukasa ];
options = {
programs.compsize = {
enable = lib.mkEnableOption "compsize + setcap wrapper";
package = lib.mkPackageOption pkgs "compsize" { };
};
};
config =
let
cfg = config.programs.compsize;
in
lib.mkIf cfg.enable {
# for the man page
environment.systemPackages = [ cfg.package ];
security.wrappers.compsize = {
owner = config.users.users.root.name;
group = config.users.users.root.group;
capabilities = "cap_sys_admin+p";
source = lib.getExe cfg.package;
};
};
}

View File

@@ -92,11 +92,6 @@ in
]
);
restartTriggers = [
cfg.package
config.environment.etc."gamemode.ini".source
];
serviceConfig.ExecStart = lib.mkIf cfg.enableRenice [
"" # Tell systemd to clear the existing ExecStart list, to prevent appending to it.
"${config.security.wrapperDir}/gamemoded"

View File

@@ -10,11 +10,6 @@ let
package = cfg.package.override {
inherit (config.security) wrapperDir;
};
uiPackage = cfg.ui.package.override {
gpu-screen-recorder = package;
inherit (config.security) wrapperDir;
};
in
{
options = {
@@ -29,47 +24,19 @@ in
wrappers for promptless recording.
'';
};
ui = {
enable = lib.mkEnableOption "the GPU Screen Recorder overlay UI";
package = lib.mkPackageOption pkgs "gpu-screen-recorder-ui" { };
notifPackage = lib.mkPackageOption pkgs "gpu-screen-recorder-notification" { };
};
};
};
config = lib.mkIf cfg.enable (
lib.mkMerge [
{
environment.systemPackages = [ cfg.package ];
config = lib.mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
security.wrappers."gsr-kms-server" = {
owner = "root";
group = "root";
capabilities = "cap_sys_admin+ep";
source = lib.getExe' package "gsr-kms-server";
};
}
security.wrappers."gsr-kms-server" = {
owner = "root";
group = "root";
capabilities = "cap_sys_admin+ep";
source = lib.getExe' package "gsr-kms-server";
};
};
(lib.mkIf cfg.ui.enable {
environment.systemPackages = [
cfg.ui.package
cfg.ui.notifPackage
];
security.wrappers."gsr-global-hotkeys" = {
owner = "root";
group = "root";
capabilities = "cap_setuid+ep";
source = lib.getExe' uiPackage "gsr-global-hotkeys";
};
})
]
);
meta.maintainers = with lib.maintainers; [
timschumi
AhmedAmr
keenanweaver
];
meta.maintainers = with lib.maintainers; [ timschumi ];
}

View File

@@ -1,93 +0,0 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.solaar;
inherit (lib)
mkEnableOption
mkIf
mkOption
types
maintainers
mkPackageOption
;
in
{
options.programs.solaar = {
enable = mkEnableOption "Solaar, the open source driver for Logitech devices.";
package = mkPackageOption pkgs "solaar" { };
userService = {
enable = mkEnableOption "Enable the solaar systemd service for each user.";
window = mkOption {
type = types.enum [
"show"
"hide"
"only"
];
default = "hide";
description = ''
Start with window showing / hidden / only (no tray icon).
'';
};
batteryIcons = mkOption {
type = types.enum [
"regular"
"symbolic"
"solaar"
];
default = "regular";
description = ''
Prefer regular battery / symbolic battery / solaar icons.
'';
};
extraArgs = mkOption {
type = types.listOf types.str;
default = [ ];
example = [ "--restart-on-wake-up" ];
description = ''
Extra arguments to pass to Solaar.
'';
};
};
};
config = mkIf cfg.enable {
hardware.logitech.wireless.enable = lib.mkDefault true;
environment.systemPackages = [ cfg.package ];
systemd.user.services.solaar = mkIf cfg.userService.enable {
description = "Solaar, the open source driver for Logitech devices";
wantedBy = [ "graphical-session.target" ];
partOf = [ "graphical-session.target" ];
after = [ "dbus.service" ];
serviceConfig = {
Type = "simple";
ExecStart = lib.escapeShellArgs (
[
(lib.getExe cfg.package)
"--window"
cfg.userService.window
"--battery-icons"
cfg.userService.batteryIcons
]
++ cfg.userService.extraArgs
);
Restart = "on-failure";
RestartSec = "5";
};
};
};
meta = {
maintainers = [ maintainers.Svenum ];
};
}

View File

@@ -1,97 +0,0 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.upki;
format = pkgs.formats.toml { };
configFile = format.generate "upki.toml" cfg.settings;
in
{
options.services.upki = {
enable = lib.mkEnableOption "upki certificate infrastructure cache updates";
package = lib.mkPackageOption pkgs "upki" { };
interval = lib.mkOption {
type = lib.types.str;
default = "2h";
example = "1h";
description = "How often to update the upki cache.";
};
settings = lib.mkOption {
inherit (format) type;
default = { };
description = "Settings written to the upki config file.";
example = lib.literalExpression ''
{
cache-dir = "/var/cache/upki";
revocation.fetch-url = "https://upki.rustls.dev/";
}
'';
};
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
services.upki.settings = {
cache-dir = lib.mkDefault "/var/cache/upki";
revocation.fetch-url = lib.mkDefault "https://upki.rustls.dev/";
};
users.users.upki = {
isSystemUser = true;
group = "upki";
};
users.groups.upki = { };
systemd.services.upki-fetch = {
description = "Update the upki cache";
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${lib.getExe cfg.package} --config-file ${configFile} fetch";
User = "upki";
Group = "upki";
CacheDirectory = "upki";
CacheDirectoryMode = "0755";
UMask = "0022";
# Hardening
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateTmp = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectSystem = "strict";
RestrictAddressFamilies = [
"AF_UNIX"
"AF_INET"
"AF_INET6"
];
RestrictRealtime = true;
SystemCallArchitectures = "native";
SystemCallErrorNumber = "EPERM";
SystemCallFilter = "@system-service";
};
};
systemd.timers.upki-fetch = {
description = "Update the upki cache every ${cfg.interval}";
wantedBy = [ "timers.target" ];
timerConfig = {
OnActiveSec = "0";
OnUnitActiveSec = cfg.interval;
};
};
};
}

View File

@@ -6,7 +6,7 @@
}:
let
inherit (lib.options) mkEnableOption mkPackageOption mkOption;
inherit (lib.modules) mkIf mkAfter;
inherit (lib.modules) mkIf;
inherit (lib.meta) getExe;
inherit (lib.types) listOf str;
inherit (lib.strings) concatStringsSep;
@@ -52,15 +52,15 @@ in
environment.systemPackages = [ cfg.package ];
programs = {
zsh.interactiveShellInit = mkIf cfg.enableZshIntegration (mkAfter ''
zsh.interactiveShellInit = mkIf cfg.enableZshIntegration ''
eval "$(${getExe cfg.package} init zsh ${cfgFlags} )"
'');
bash.interactiveShellInit = mkIf cfg.enableBashIntegration (mkAfter ''
'';
bash.interactiveShellInit = mkIf cfg.enableBashIntegration ''
eval "$(${getExe cfg.package} init bash ${cfgFlags} )"
'');
fish.interactiveShellInit = mkIf cfg.enableFishIntegration (mkAfter ''
'';
fish.interactiveShellInit = mkIf cfg.enableFishIntegration ''
${getExe cfg.package} init fish ${cfgFlags} | source
'');
'';
xonsh.config = ''
execx($(${getExe cfg.package} init xonsh ${cfgFlags}), 'exec', __xonsh__.ctx, filename='zoxide')
'';

View File

@@ -79,7 +79,7 @@ in
};
firstUid = mkOption {
type = types.ints.between 1000 65533;
type = types.numbers.between 1000 65533;
default = 60000;
description = ''
Start of block of UIDs reserved for sandboxes.
@@ -87,7 +87,7 @@ in
};
firstGid = mkOption {
type = types.ints.between 1000 65533;
type = types.numbers.between 1000 65533;
default = 60000;
description = ''
Start of block of GIDs reserved for sandboxes.
@@ -95,7 +95,7 @@ in
};
numBoxes = mkOption {
type = types.ints.between 1000 65533;
type = types.numbers.between 1000 65533;
default = 1000;
description = ''
Number of UIDs and GIDs to reserve, starting from

View File

@@ -84,10 +84,12 @@ let
}
}
'';
__structuredAttrs = true;
passAsFile = [
"expectScript"
];
};
checkPhase = ''
printf "%s" "$expectScript" | ${lib.getExe pkgs.buildPackages.expect} -f -
${lib.getExe pkgs.buildPackages.expect} -f "$expectScriptPath"
'';
};

View File

@@ -108,7 +108,7 @@ in
DynamicUser = true;
ExecStart = lib.getExe cfg.package;
LockPersonality = true;
MemoryDenyWriteExecute = false; # pcre2 jit
MemoryDenyWriteExecute = true;
PrivateDevices = true;
ProtectClock = true;
ProtectControlGroups = true;

View File

@@ -1,529 +0,0 @@
{
config,
lib,
pkgs,
utils,
...
}:
let
inherit (lib)
foldlAttrs
literalExpression
literalMD
mkEnableOption
mkIf
mkOption
mkPackageOption
mkRemovedOptionModule
mkRenamedOptionModule
nameValuePair
optionals
types
;
cfg = config.services.forgejo-runner;
settingsFormat = pkgs.formats.yaml { };
config' = config;
# An option type for cfg.secrets that is like cfg.settings (free-form yaml), but limited
# to just path and attrsOf path. It uses the same implementation primivites as
# pkgs.formats.yaml (serializableValueWith).
# The alternative would be something like types.any, which is too weak, or a hard-coded
# max-depth by chaining multiple types.oneOf and types.attrsOf together, which is both
# longer in code and less future-proof.
secretsTypeBase = types.oneOf [
types.externalPath
(types.attrsOf secretsTypeBase)
];
secretsType = secretsTypeBase // {
description = "nested attribute set of ${types.externalPath.description}";
};
labels =
instance:
instance.settings.runner.labels
++ (lib.flatten (
lib.mapAttrsToList (_: value: value.labels or [ ]) instance.settings.server.connections
));
in
{
meta.maintainers = pkgs.forgejo-runner.meta.maintainers;
options.services.forgejo-runner = {
package = mkPackageOption pkgs "forgejo-runner" { };
instances = mkOption {
default = { };
description = ''
Forgejo Runner instances.
'';
type = types.attrsOf (
types.submodule (
{
options,
config,
name,
...
}:
{
imports = [
../../misc/assertions.nix
# compat for users coming from nixos/gitea-actions-runner
(mkRenamedOptionModule [ "url" ] [ "settings" "server" "connections" "default" "url" ])
(mkRenamedOptionModule [ "labels" ] [ "settings" "runner" "labels" ])
(mkRemovedOptionModule [ "name" ] ''
The option `${options.name}' has been removed, because it has no longer
any effect, as runners no longer self-report their name to Forgejo.
'')
(mkRemovedOptionModule [ "token" ] ''
The option `${options.token}' has been renamed to
`${options.settings}.server.connections.default.token'
but additional attention is required.
Assuming you are migrating from `services.gitea-actions-runner', you will need to:
1. Find the old `.runner' file of your previously registered runner. You will need
to extract two values from it. Given the instance name of "${name}", you should
be able to find it at `/var/lib/gitea-runner/${name}/.runner'.
2. Read the contents of it, for example using `cat /var/lib/gitea-runner/native/.runner'.
3. Take note of the "uuid" and set the option `${options.settings}.server.connections.default.uuid'
to that value. For example "c9e50be9-a7c3-4aee-ba35-624c4ff8c519".
4. Take note of the "token" and set the option `${options.settings}.server.connections.default.token'
to that value. For example "6634bb58be0db23cc013a2e72dd1828ae0257cf".
5. Remove option `${options.token}'.
'')
(mkRemovedOptionModule [ "tokenFile" ] ''
The option `${options.tokenFile}' has been renamed to
`${options.secrets}.server.connections.default.token_url'
but additional attention is required.
Assuming you are migrating from `services.gitea-actions-runner', you will need to:
1. Find the old `.runner' file of your previously registered runner. You will need
to extract two values from it. Given the instance name of "${name}", you should
be able to find it at `/var/lib/gitea-runner/${name}/.runner'.
2. Read the contents of it, for example using `cat /var/lib/gitea-runner/native/.runner'.
3. Take note of the "uuid" and set the option `${options.settings}.server.connections.default.uuid'
to that value. For example "c9e50be9-a7c3-4aee-ba35-624c4ff8c519".
4. Take note of the "token" and replace the contents of your existing token file with it.
You no longer need to prefix the token with `TOKEN='. Put just the token in that file
and nothing else.
5. Rename `${options.tokenFile}' to `${options.secrets}.server.connections.default.token_url'.
'')
];
config = mkIf config.enable {
assertions = [
{
assertion =
lib.any (label: lib.hasInfix ":docker" label) (labels config)
-> (
config.runtimes.docker
|| config.runtimes.podman
# Mute assertion as an escape hatch for end-users
# that override our options.runtimes default.
|| options.runtimes.docker.highestPrio < (lib.mkOptionDefault { }).priority
|| options.runtimes.podman.highestPrio < (lib.mkOptionDefault { }).priority
);
message = ''
The option `${options.settings}' has at least one label of
type `docker' configured, but no compatible container runtime enabled.
You need to enable either
`config.virtualisation.docker.enable' or
`config.virtualisation.podman.enable'.
If you are absolutely sure what you are doing
and are certain this is wrong, you can set
`${options.runtimes.docker}' or
`${options.runtimes.podman}' to dismiss this assertion.
'';
}
{
assertion = config.settings.server.connections != { };
message = ''
The option `${options.settings}.server.connections' requires at least one connection.
'';
}
]
++ (foldlAttrs (
assertions: _: connection:
assertions ++ connection.assertions
) [ ] config.settings.server.connections);
};
options = {
enable = mkEnableOption "this Forgejo Runner instance";
settings = mkOption {
default = { };
description = ''
Free-form settings written directly to the {file}`config.yaml` file.
Refer to [`config.example.yaml`] or run {command}`forgejo-runner generation-config` for supported values.
[`config.example.yaml`]: https://code.forgejo.org/forgejo/runner/src/branch/main/internal/pkg/config/config.example.yaml
'';
type = types.submodule {
freeformType = settingsFormat.type;
config = lib.mapAttrsRecursive (
path: _: "file:$CREDENTIALS_DIRECTORY/${lib.join "__" path}"
) config.secrets;
options = {
runner = {
labels = mkOption {
# TODO: Support new attrset format (yaml map)
# https://code.forgejo.org/forgejo/runner/pulls/1571
type = types.listOf types.str;
example = literalExpression ''
[
# provide a debian base with nodejs for actions
"debian-latest:docker://node:current"
# fake the ubuntu name, because node provides no ubuntu builds
"ubuntu-latest:docker://node:current"
# provide native execution on the host
#"native:host"
]
'';
description = ''
Labels used to map jobs to their runtime environment.
Many common actions require {command}`bash`, {command}`git` and {command}`node`,
as well as a filesystem that follows the filesystem hierarchy standard.
If you specify a label of type `docker`, the resulting runner service
will be automatically added to the *Podman* or *Docker* group.
See <https://forgejo.org/docs/latest/admin/actions/configuration/#choosing-labels>.
::: {.note}
Labels of type [`lxc`] are currently not supported.
:::
[`lxc`]: https://forgejo.org/docs/latest/admin/actions/configuration/#lxc
'';
};
};
server = {
connections = mkOption {
default = { };
description = ''
One or more connections to Forgejo instances, each with a UUID and Token pair.
See <https://forgejo.org/docs/latest/admin/actions/registration/>.
::: {.note}
Ephemeral runner mode is not yet supported by this module.
:::
'';
example = literalExpression ''
{
default = {
url = "https://example.com/";
uuid = "c9e50be9-a7c3-4aee-ba35-624c4ff8c519";
# Also see ${options.secrets}.server.connections.<name>.token_url
token = "6634bb58be0db23cc013a2e72dd1828ae0257cf";
};
}
'';
type = types.attrsOf (
types.submodule (
{ name, config, ... }:
{
freeformType = settingsFormat.type;
imports = [
../../misc/assertions.nix
];
config = {
assertions = [
{
assertion =
(config.token == null && config ? token_url && config.token_url != null)
|| (config.token != null && config ? token_url && config.token_url == null)
|| (config.token != null && !config ? token_url);
message = ''
The option `${options.settings}' needs to have exactly one of
`server.connections.${name}.token_url': ${
if config ? token_url then lib.toJSON config.token_url else "<not set>"
} or
`server.connections.${name}.token': ${lib.toJSON config.token}
that is *not* null.
Hint:
`${options.secrets}.server.connections.${name}.token_url' will set
`${options.settings}.server.connections.${name}.token_url' for you.
'';
}
];
};
options = {
url = mkOption {
type = types.str;
example = "https://example.com/";
description = ''
Base URL of your Forgejo instance.
'';
};
uuid = mkOption {
type = types.str;
example = "c9e50be9-a7c3-4aee-ba35-624c4ff8c519";
description = ''
UUID of this runner.
See <https://forgejo.org/docs/latest/admin/actions/registration/>.
'';
};
token = mkOption {
type = types.nullOr types.str;
example = "6634bb58be0db23cc013a2e72dd1828ae0257cf";
description = ''
Token of this runner.
See <https://forgejo.org/docs/latest/admin/actions/registration/>.
::: {.note}
The deprecated "Registration Token" is not supported.
You need a UUID and Token pair.
:::
::: {.warning}
The value will be stored unencrypted in the world-readable Nix store.
To store the secret securely, see {option}`${options.secrets}.server.connections.<name>.token_url`.
:::
'';
};
};
}
)
);
};
};
};
};
};
secrets = mkOption {
type = secretsType;
default = { };
description = ''
This follows the same structure as {option}`${options.settings}`
but the value of each key is a path.
The specified secret path is then read by systemd via [`LoadCredential=`]
and templated into {option}`${options.settings}` for you.
[`LoadCredential=`]: https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#Credentials
'';
example = literalExpression ''
{
server.connections.example = {
token_url = "/run/keys/forgejo-runner_token";
};
cache = {
secret_url = "/run/keys/forgejo-runner_cache-secret";
};
}
'';
};
hostPackages = mkOption {
type = types.listOf types.package;
default = with pkgs; [
bash
coreutils
curl
gawk
gnused
nodejs
wget
];
defaultText = literalExpression ''
with pkgs; [
bash
coreutils
curl
gawk
gnused
nodejs
wget
]
'';
description = ''
List of packages that are available to your workflow and actions when the
runner is configured with a label of type `host` ({option}`${options.runtimes.host}`).
::: {.note}
{command}`gitMinimal` is always part of the environment because {command}`forgejo-runner`
depends on it. If you need a different variant of {command}`git`, e.g. {command}`gitFull`,
add it here. Your package will take priority over {command}`gitMinimal`.
:::
'';
};
runtimes = {
host = mkOption {
type = types.bool;
default = lib.any (label: lib.hasSuffix ":host" label) (labels config);
defaultText = literalMD "Whether this instance has at least one label with suffix `:host`.";
description = ''
Whether to configure the systemd service for jobs with the backend of type `host`.
::: {.warning}
Setting this will override the automatic detection and safeguards.
:::
'';
};
docker = mkOption {
type = types.bool;
default =
lib.any (label: lib.hasInfix ":docker" label) (labels config)
&& config'.virtualisation.docker.enable;
defaultText = literalMD ''
Whether this instance has at least one label with infix `:docker`
and {option}`config.virtualisation.docker.enable` set to `true`.
'';
description = ''
Whether to configure the systemd service to work with Docker.
::: {.warning}
Setting this will override the automatic detection and safeguards.
:::
'';
};
podman = mkOption {
type = types.bool;
default =
lib.any (label: lib.hasInfix ":docker" label) (labels config)
&& config'.virtualisation.podman.enable;
defaultText = literalMD ''
Whether this instance has at least one label with infix `:docker`
and {option}`config.virtualisation.podman.enable` set to `true`.
'';
description = ''
Whether to configure the systemd service to work with Podman.
::: {.warning}
Setting this will override the automatic detection and safeguards.
:::
'';
};
};
configFile = mkOption {
internal = true;
readOnly = true;
type = types.path;
default = settingsFormat.generate "config.yaml" (
# Filter out remains of ../misc/assertions.nix.
# Note: This is for optics only, as forgejo-runner simply ignores settings it does not know.
lib.filterAttrsRecursive (n: _: n != "assertions" && n != "warnings") config.settings
);
description = ''
Implementation detail for use in {file}`nixos/tests/forgejo.nix`.
FIXME: Offload into top-level config once upstream supports {var}`uuid_url`.
'';
};
};
}
)
);
};
};
config = mkIf (cfg.instances != { }) {
assertions = (
foldlAttrs (
assertions: _: instance:
assertions ++ instance.assertions
) [ ] cfg.instances
);
warnings = (
foldlAttrs (
warnings: _: instance:
warnings ++ instance.warnings
) [ ] cfg.instances
);
systemd.services = lib.mapAttrs' (
name: instance:
nameValuePair "forgejo-runner-${utils.escapeSystemdPath name}" {
inherit (instance) enable;
description = "Forgejo Runner";
wants = [ "network-online.target" ];
after = [
"network-online.target"
]
++ optionals instance.runtimes.docker [
"docker.service"
]
++ optionals instance.runtimes.podman [
# TODO: Add support for rootless Podman
"podman.service"
];
wantedBy = [
"multi-user.target"
];
environment = {
HOME = "/var/lib/forgejo-runner/${name}";
};
path = optionals instance.runtimes.host instance.hostPackages ++ [ pkgs.gitMinimal ];
serviceConfig = {
DynamicUser = true;
StateDirectory = "forgejo-runner/${name}";
WorkingDirectory = "/var/lib/forgejo-runner/${name}";
# DynamicUser will try to use id-mapped mounts for exec directories,
# which has the side-effect of setting nosuid and noexec as mount option.
# Users of host runners expect to be able to execute scripts in their
# pipeline, so we override the noexec mount option by setting ExecPaths.
ExecPaths = optionals instance.runtimes.host [ "/var/lib/forgejo-runner/${name}" ];
ExecStart = toString [
(lib.getExe cfg.package)
"daemon"
"--config"
instance.configFile
];
Restart = "on-failure";
RestartSec = 10;
LoadCredential = lib.mapAttrsToListRecursive (
path: value: "${lib.join "__" path}:${value}"
) instance.secrets;
SupplementaryGroups =
optionals instance.runtimes.docker [
"docker"
]
++ optionals instance.runtimes.podman [
"podman"
];
};
}
) cfg.instances;
};
}

View File

@@ -1,88 +0,0 @@
# shellcheck shell=bash
# Request an installation access token for a GitHub App.
#
# Adapted from https://github.com/myoung34/docker-github-actions-runner (MIT,
# Copyright (c) 2020 Marcus Young), see:
# https://github.com/orgs/community/discussions/24743#discussioncomment-3245300
#
# Expects the following environment variables:
# * APP_ID the GitHub App's ID
# * APP_PRIVATE_KEY the GitHub App's PEM-encoded private key (contents)
# * APP_LOGIN the org/user login the App is installed on
# * GITHUB_HOST optional, defaults to github.com (set for GHES)
#
# Prints the installation access token (prefixed with `ghs_`) to stdout.
_GITHUB_HOST=${GITHUB_HOST:="github.com"}
# If the host is not github.com, use the GitHub Enterprise Server API endpoint.
if [[ ${_GITHUB_HOST} == "github.com" ]]; then
URI="https://api.${_GITHUB_HOST}"
else
URI="https://${_GITHUB_HOST}/api/v3"
fi
API_VERSION=v3
API_HEADER="Accept: application/vnd.github.${API_VERSION}+json"
CONTENT_LENGTH_HEADER="Content-Length: 0"
APP_INSTALLATIONS_URI="${URI}/app/installations"
# JWT token issuance and expiration parameters, see:
# https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-json-web-token-jwt-for-a-github-app
JWT_IAT_DRIFT=60
JWT_EXP_DELTA=600
JWT_JOSE_HEADER='{
"alg": "RS256",
"typ": "JWT"
}'
build_jwt_payload() {
now=$(date +%s)
iat=$((now - JWT_IAT_DRIFT))
jq -c \
--arg iat_str "${iat}" \
--arg exp_delta_str "${JWT_EXP_DELTA}" \
--arg app_id_str "${APP_ID}" \
'
($iat_str | tonumber) as $iat
| ($exp_delta_str | tonumber) as $exp_delta
| ($app_id_str | tonumber) as $app_id
| .iat = $iat
| .exp = ($iat + $exp_delta)
| .iss = $app_id
' <<<"{}" | tr -d '\n'
}
base64url() {
base64 | tr '+/' '-_' | tr -d '=\n'
}
rs256_sign() {
openssl dgst -binary -sha256 -sign <(echo "$1")
}
request_access_token() {
jwt_payload=$(build_jwt_payload)
encoded_jwt_parts=$(base64url <<<"${JWT_JOSE_HEADER}").$(base64url <<<"${jwt_payload}")
encoded_mac=$(echo -n "${encoded_jwt_parts}" | rs256_sign "${APP_PRIVATE_KEY}" | base64url)
generated_jwt="${encoded_jwt_parts}.${encoded_mac}"
auth_header="Authorization: Bearer ${generated_jwt}"
app_installations_response=$(
curl -fsSX GET \
-H "${auth_header}" \
-H "${API_HEADER}" \
"${APP_INSTALLATIONS_URI}"
)
access_token_url=$(echo "${app_installations_response}" | jq --raw-output '.[] | select (.account.login == "'"${APP_LOGIN}"'" and .app_id == '"${APP_ID}"') .access_tokens_url')
curl -fsSX POST \
-H "${CONTENT_LENGTH_HEADER}" \
-H "${auth_header}" \
-H "${API_HEADER}" \
"${access_token_url}" |
jq --raw-output .token
}
request_access_token

View File

@@ -42,8 +42,7 @@
};
url = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
type = lib.types.str;
description = ''
Repository to add the runner to.
@@ -56,16 +55,12 @@
Otherwise, you are going to get a `404 NotFound`
from `POST https://api.github.com/actions/runner-registration`
in the configure script.
Mandatory unless `orgs` is used, in which case the URL is taken
from each `orgs.<name>.url` instead and this option is ignored.
'';
example = "https://github.com/nixos/nixpkgs";
};
tokenFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
type = lib.types.path;
description = ''
The full path to a file which contains either
@@ -73,10 +68,6 @@
* a classic PAT
* or a runner registration token
Exactly one of `tokenFile` and `githubApp` must be set. Use
`githubApp` to authenticate via a GitHub App installation instead
of a token file.
Changing this option or the `tokenFile`s content triggers a new runner registration.
We suggest using the fine-grained PATs. A runner registration token is valid
@@ -135,71 +126,6 @@
default = "auto";
};
githubApp = lib.mkOption {
default = null;
description = ''
Authenticate the runner using a GitHub App installation instead
of a `tokenFile`. Exactly one of `tokenFile` and `githubApp` must
be set.
On every start the service derives a short-lived installation
access token from the App's private key, uses it to fetch a fresh
runner registration token and registers the runner with it. This
avoids storing a long-lived personal access token on the host and
pairs well with `ephemeral` runners.
The App needs read and write access to the
"self-hosted runners" administration of the organisation (or
repository) given in `url`, and must be installed on the `login`
below.
'';
example = lib.literalExpression ''
{
id = 123456;
login = "my-org";
privateKeyFile = "/run/secrets/github-app.pem";
}
'';
type = lib.types.nullOr (
lib.types.submodule {
options = {
id = lib.mkOption {
type = lib.types.int;
description = "The GitHub App's ID (the numeric `App ID`, not the client ID).";
example = 123456;
};
login = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = ''
The organisation (or user) login the GitHub App is
installed on. Used to look up the App installation and,
for organisation-wide runners, as the registration scope.
Mandatory unless `orgs` is used, in which case the login
is taken from each `orgs.<name>.login` instead and this
option is ignored.
Changing this option triggers a new runner registration.
'';
example = "my-org";
};
privateKeyFile = lib.mkOption {
type = lib.types.path;
description = ''
The full path to a file containing the GitHub App's
PEM-encoded private key. The file should be deployed as a
secret and is never copied into the Nix store.
'';
example = "/run/secrets/github-app.pem";
};
};
}
);
};
name = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = ''
@@ -211,107 +137,6 @@
default = name;
};
count = lib.mkOption {
type = lib.types.ints.positive;
default = 1;
example = 4;
description = ''
Number of identical runner instances to create.
Without `orgs`, this fans the single entry (using the
entry-level `url`) out into `count` runner services:
`github-runner-<name>` for `count == 1` (unchanged) and
`github-runner-<name>-<n>` for `count > 1`. Each instance
registers under a distinct runner name.
With `orgs`, this is the default replica count for every org
that does not set its own; see `orgs.<name>.count`.
Pairs well with `ephemeral`.
'';
};
orgs = lib.mkOption {
default = { };
description = ''
Organisations (or repositories) to serve from this entry.
When set, the entry fans out into one systemd service per runner
named `github-runner-<name>-<org>-<n>`, where `<org>` is the
attribute name and `<n>` ranges over the per-org `count`. The
entry-level `githubApp`/`tokenFile` is shared across every org;
only the App `login` changes per org (defaulting to the
attribute name), so a single GitHub App installed on multiple
orgs serves all of them.
Leaving this empty (the default) keeps the single-runner
behaviour: the entry-level `url`, `name` and auth, fanned out by
the entry-level `count`.
'';
example = lib.literalExpression ''
{
org-a.count = 12;
org-b = {
count = 2;
extraLabels = [ "org-b" ];
};
}
'';
type = lib.types.attrsOf (
lib.types.submodule (
{ name, ... }:
{
options = {
url = lib.mkOption {
type = lib.types.str;
default = "https://github.com/${name}";
defaultText = lib.literalExpression ''"https://github.com/''${name}"'';
description = ''
URL of the organisation (or repository) to connect to.
Defaults to the GitHub URL derived from the attribute name.
'';
};
login = lib.mkOption {
type = lib.types.str;
default = name;
defaultText = lib.literalExpression "\${name}";
description = ''
GitHub login (org or user) the shared `githubApp` is
installed on. Defaults to the attribute name. Ignored
when authenticating via `tokenFile`.
'';
};
count = lib.mkOption {
type = lib.types.ints.positive;
default = config.count;
defaultText = lib.literalMD "the entry-level `count`";
example = 4;
description = ''
Number of identical runner instances to create for this
org. Defaults to the entry-level `count`. Each gets its
own systemd service named `github-runner-<name>-<org>-<n>`
and registers under a distinct runner name. Pairs well
with `ephemeral`.
'';
};
extraLabels = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
example = lib.literalExpression ''[ "org-a" ]'';
description = ''
Extra labels added, on top of the entry-level `extraLabels`,
only to this org's runners.
'';
};
};
}
)
);
};
runnerGroup = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = ''

View File

@@ -1,58 +0,0 @@
# shellcheck shell=bash
# Fetch a self-hosted runner registration token from the GitHub API.
#
# Adapted from https://github.com/myoung34/docker-github-actions-runner (MIT,
# Copyright (c) 2020 Marcus Young).
#
# Expects the following environment variables:
# * ACCESS_TOKEN a token authorized to manage self-hosted runners
# (a GitHub App installation token or a suitable PAT)
# * RUNNER_SCOPE one of `org`, `ent` or `repo`
# * ORG_NAME the org login (for `org` scope)
# * ENTERPRISE_NAME the enterprise slug (for `ent` scope)
# * REPO_URL the repository URL (for `repo` scope)
# * GITHUB_HOST optional, defaults to github.com (set for GHES)
#
# Prints `{"token": ..., "full_url": ...}` to stdout.
_GITHUB_HOST=${GITHUB_HOST:="github.com"}
# If the host is not github.com, use the GitHub Enterprise Server API endpoint.
if [[ ${_GITHUB_HOST} == "github.com" ]]; then
URI="https://api.${_GITHUB_HOST}"
else
URI="https://${_GITHUB_HOST}/api/v3"
fi
API_VERSION=v3
API_HEADER="Accept: application/vnd.github.${API_VERSION}+json"
AUTH_HEADER="Authorization: token ${ACCESS_TOKEN}"
CONTENT_LENGTH_HEADER="Content-Length: 0"
case ${RUNNER_SCOPE} in
org*)
_FULL_URL="${URI}/orgs/${ORG_NAME}/actions/runners/registration-token"
;;
ent*)
_FULL_URL="${URI}/enterprises/${ENTERPRISE_NAME}/actions/runners/registration-token"
;;
*)
_PROTO="https://"
_URL="${REPO_URL/${_PROTO}/}"
_PATH="$(echo "${_URL}" | grep / | cut -d/ -f2-)"
_ACCOUNT="$(echo "${_PATH}" | cut -d/ -f1)"
_REPO="$(echo "${_PATH}" | cut -d/ -f2)"
_FULL_URL="${URI}/repos/${_ACCOUNT}/${_REPO}/actions/runners/registration-token"
;;
esac
RUNNER_TOKEN="$(curl -fsSX POST \
-H "${CONTENT_LENGTH_HEADER}" \
-H "${AUTH_HEADER}" \
-H "${API_HEADER}" \
"${_FULL_URL}" |
jq -r '.token')"
echo "{\"token\": \"${RUNNER_TOKEN}\", \"full_url\": \"${_FULL_URL}\"}"

View File

@@ -1,71 +0,0 @@
# shellcheck shell=bash
# Force-remove a previously registered, offline self-hosted runner via the
# GitHub API. Used before re-registering to avoid orphaned runners piling up in
# the GitHub Actions UI.
#
# Adapted from https://github.com/myoung34/docker-github-actions-runner (MIT,
# Copyright (c) 2020 Marcus Young).
#
# Expects the following environment variables:
# * ACCESS_TOKEN a token authorized to manage self-hosted runners
# * RUNNER_NAME the name of the runner to remove
# * RUNNER_SCOPE one of `org`, `ent` or `repo`
# * ORG_NAME the org login (for `org` scope)
# * ENTERPRISE_NAME the enterprise slug (for `ent` scope)
# * REPO_URL the repository URL (for `repo` scope)
# * GITHUB_HOST optional, defaults to github.com (set for GHES)
_GITHUB_HOST=${GITHUB_HOST:="github.com"}
# If the host is not github.com, use the GitHub Enterprise Server API endpoint.
if [[ ${_GITHUB_HOST} == "github.com" ]]; then
URI="https://api.${_GITHUB_HOST}"
else
URI="https://${_GITHUB_HOST}/api/v3"
fi
API_HEADER="Accept: application/vnd.github+json"
AUTH_HEADER="Authorization: token ${ACCESS_TOKEN}"
CONTENT_LENGTH_HEADER="Content-Length: 0"
runners_url() {
case ${RUNNER_SCOPE} in
org*)
echo "${URI}/orgs/${ORG_NAME}/actions/runners"
;;
ent*)
echo "${URI}/enterprises/${ENTERPRISE_NAME}/actions/runners"
;;
*)
_PROTO="https://"
_URL="${REPO_URL/${_PROTO}/}"
_PATH="$(echo "${_URL}" | grep / | cut -d/ -f2-)"
_ACCOUNT="$(echo "${_PATH}" | cut -d/ -f1)"
_REPO="$(echo "${_PATH}" | cut -d/ -f2)"
echo "${URI}/repos/${_ACCOUNT}/${_REPO}/actions/runners"
;;
esac
}
_RUNNERS_URL="$(runners_url)"
RUNNERS="$(curl -fsSX GET \
-H "${CONTENT_LENGTH_HEADER}" \
-H "${AUTH_HEADER}" \
-H "${API_HEADER}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"${_RUNNERS_URL}")"
RUNNER_ID=$(echo "$RUNNERS" | jq -r '.runners[] | select( (.name == env.RUNNER_NAME) and (.status == "offline") ) | .id')
if [[ $RUNNER_ID == "" ]]; then
echo "Runner ${RUNNER_NAME} doesn't exist or is online. Nothing to unregister."
exit 0
fi
echo "${RUNNER_NAME} is still registered and offline. Forcing removal..."
curl -fsSX DELETE \
-H "${CONTENT_LENGTH_HEADER}" \
-H "${AUTH_HEADER}" \
-H "${API_HEADER}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"${_RUNNERS_URL}/${RUNNER_ID}"

View File

@@ -9,22 +9,6 @@
lib.flip lib.mapAttrsToList config.services.github-runners (
name: cfg:
map (lib.mkIf cfg.enable) [
{
assertion = (cfg.tokenFile == null) != (cfg.githubApp == null);
message = "`services.github-runners.${name}`: Exactly one of `tokenFile` and `githubApp` must be set";
}
{
assertion = cfg.orgs != { } || cfg.url != null;
message = "`services.github-runners.${name}`: `url` must be set unless `orgs` is used";
}
{
assertion = cfg.orgs != { } || cfg.githubApp == null || cfg.githubApp.login != null;
message = "`services.github-runners.${name}`: `githubApp.login` must be set unless `orgs` is used (the login is then derived per org)";
}
{
assertion = cfg.orgs != { } || cfg.count == 1 || cfg.name != null;
message = "`services.github-runners.${name}`: `name` must not be null when `count > 1` (each replica needs a distinct registration name)";
}
{
assertion = !cfg.noDefaultLabels || (cfg.extraLabels != [ ]);
message = "`services.github-runners.${name}`: The `extraLabels` option is mandatory if `noDefaultLabels` is set";
@@ -40,49 +24,8 @@
config.systemd.services =
let
enabledRunners = lib.filterAttrs (_: cfg: cfg.enable) config.services.github-runners;
runnerInstances = lib.concatMapAttrs (
name: cfg:
if cfg.orgs == { } then
# Single org/repo (entry-level `url`), fanned out by `count`. For
# `count == 1` the service keeps the bare `github-runner-<name>` name
# for backwards compatibility; `count > 1` suffixes `-<n>`.
let
suffixes = if cfg.count == 1 then [ "" ] else map (n: "-${toString n}") (lib.range 1 cfg.count);
in
lib.listToAttrs (
map (
suffix:
lib.nameValuePair "${name}${suffix}" (
cfg // { name = if cfg.name == null then null else "${cfg.name}${suffix}"; }
)
) suffixes
)
else
lib.listToAttrs (
lib.concatLists (
lib.flip lib.mapAttrsToList cfg.orgs (
orgName: org:
map (
n:
let
key = "${name}-${orgName}-${toString n}";
in
lib.nameValuePair key (
cfg
// {
url = org.url;
name = key;
extraLabels = cfg.extraLabels ++ org.extraLabels;
githubApp = if cfg.githubApp == null then null else cfg.githubApp // { login = org.login; };
}
)
) (lib.range 1 org.count)
)
)
)
) enabledRunners;
in
(lib.flip lib.mapAttrs' runnerInstances (
(lib.flip lib.mapAttrs' enabledRunners (
name: cfg:
let
svcName = "github-runner-${name}";
@@ -98,66 +41,6 @@
currentConfigTokenFilename = ".current-token";
workDir = if cfg.workDir == null then runtimeDir else cfg.workDir;
newConfigTokenPath = "$STATE_DIRECTORY/.new-token";
currentConfigTokenPath = "$STATE_DIRECTORY/${currentConfigTokenFilename}";
# Wrapper script which expects the full path of the state, working and logs
# directory as arguments. Overrides the respective systemd variables to provide
# unambiguous directory names. This becomes relevant, for example, if the
# caller overrides any of the StateDirectory=, RuntimeDirectory= or LogDirectory=
# to contain more than one directory. This causes systemd to set the respective
# environment variables with the path of all of the given directories, separated
# by a colon.
writeScript =
scriptName: lines:
pkgs.writeShellScript "${svcName}-${scriptName}.sh" ''
set -euo pipefail
STATE_DIRECTORY="$1"
WORK_DIRECTORY="$2"
LOGS_DIRECTORY="$3"
${lines}
'';
ghUrlPath = lib.removePrefix "https://github.com/" cfg.url;
ghUrlSegments = lib.filter (s: s != "") (lib.splitString "/" ghUrlPath);
runnerScope = if lib.length ghUrlSegments >= 2 then "repo" else "org";
appHelper =
helperName: scriptFile:
pkgs.writeShellApplication {
name = helperName;
runtimeInputs = with pkgs; [
jq
curl
openssl
coreutils
];
excludeShellChecks = [
"SC2154"
"SC2116"
];
text = builtins.readFile scriptFile;
};
fetchAccessToken = appHelper "github-app-access-token" ./app-token.sh;
fetchRegistrationToken = appHelper "github-runner-registration-token" ./registration-token.sh;
removeRunner = appHelper "github-runner-remove" ./remove-runner.sh;
appEnv = lib.optionalString (cfg.githubApp != null) ''
export APP_ID=${toString cfg.githubApp.id}
export APP_LOGIN=${lib.escapeShellArg cfg.githubApp.login}
APP_PRIVATE_KEY="$(cat ${lib.escapeShellArg cfg.githubApp.privateKeyFile})"
export APP_PRIVATE_KEY
export RUNNER_SCOPE=${runnerScope}
export ORG_NAME=${lib.escapeShellArg cfg.githubApp.login}
export REPO_URL=${lib.escapeShellArg cfg.url}
${
if cfg.name != null then
"export RUNNER_NAME=${lib.escapeShellArg cfg.name}"
else
''export RUNNER_NAME="$(uname -n)"''
}
'';
in
lib.nameValuePair svcName {
description = "GitHub Actions runner";
@@ -201,6 +84,24 @@
# - Set up the directory structure by creating the necessary symlinks.
ExecStartPre =
let
# Wrapper script which expects the full path of the state, working and logs
# directory as arguments. Overrides the respective systemd variables to provide
# unambiguous directory names. This becomes relevant, for example, if the
# caller overrides any of the StateDirectory=, RuntimeDirectory= or LogDirectory=
# to contain more than one directory. This causes systemd to set the respective
# environment variables with the path of all of the given directories, separated
# by a colon.
writeScript =
name: lines:
pkgs.writeShellScript "${svcName}-${name}.sh" ''
set -euo pipefail
STATE_DIRECTORY="$1"
WORK_DIRECTORY="$2"
LOGS_DIRECTORY="$3"
${lines}
'';
runnerRegistrationConfig = lib.getAttrs [
"ephemeral"
"extraLabels"
@@ -213,6 +114,8 @@
] cfg;
newConfigPath = builtins.toFile "${svcName}-config.json" (builtins.toJSON runnerRegistrationConfig);
currentConfigPath = "$STATE_DIRECTORY/.nixos-current-config.json";
newConfigTokenPath = "$STATE_DIRECTORY/.new-token";
currentConfigTokenPath = "$STATE_DIRECTORY/${currentConfigTokenFilename}";
runnerCredFiles = [
".credentials"
@@ -261,23 +164,6 @@
# Always clean workDir
find -H "$WORK_DIRECTORY" -mindepth 1 -delete
'';
unconfigureRunnerGitHubApp = writeScript "unconfigure-github-app" ''
${appEnv}
ACCESS_TOKEN="$(${lib.getExe' fetchAccessToken "github-app-access-token"})"
export ACCESS_TOKEN
${lib.getExe' removeRunner "github-runner-remove"} || true
find "$STATE_DIRECTORY/" -mindepth 1 -delete
umask 000
${lib.getExe' fetchRegistrationToken "github-runner-registration-token"} \
| ${pkgs.jq}/bin/jq -r '.token' > "${newConfigTokenPath}"
install --mode=600 "${newConfigTokenPath}" "${currentConfigTokenPath}"
# Always clean workDir
find -H "$WORK_DIRECTORY" -mindepth 1 -delete
'';
configureRunner =
writeScript "configure" # bash
''
@@ -299,33 +185,24 @@
${lib.optionalString cfg.noDefaultLabels "--no-default-labels"}
)
token=$(<"${newConfigTokenPath}")
${
if cfg.githubApp != null then
''
args+=(--token "$token")
''
case ${cfg.tokenType} in
access)
args+=(--pat "$token")
;;
registration)
args+=(--token "$token")
;;
auto)
# If the token file contains a PAT (i.e., it starts with "ghp_" or "github_pat_"),
# we have to use the --pat option, if it is not a PAT, we assume it contains a
# registration token and use the --token option
if [[ "$token" =~ ^gh[a-z]+_* ]] || [[ "$token" =~ ^github_pat_* ]]; then
args+=(--pat "$token")
else
''
case ${cfg.tokenType} in
access)
args+=(--pat "$token")
;;
registration)
args+=(--token "$token")
;;
auto)
# If the token file contains a PAT (i.e., it starts with "ghp_" or "github_pat_"),
# we have to use the --pat option, if it is not a PAT, we assume it contains a
# registration token and use the --token option
if [[ "$token" =~ ^gh[a-z]+_* ]] || [[ "$token" =~ ^github_pat_* ]]; then
args+=(--pat "$token")
else
args+=(--token "$token")
fi
;;
esac
''
}
args+=(--token "$token")
fi
;;
esac
${cfg.package}/bin/Runner.Listener configure "''${args[@]}"
# Move the automatically created _diag dir to the logs dir
mkdir -p "$STATE_DIRECTORY/_diag"
@@ -357,33 +234,11 @@
}"
)
[
# runs as root
"+${if cfg.githubApp != null then unconfigureRunnerGitHubApp else unconfigureRunner}"
"+${unconfigureRunner}" # runs as root
configureRunner
setupWorkDir
];
ExecStopPost = lib.optionals (cfg.githubApp != null) (
let
unregister = writeScript "unregister-github-app" ''
${appEnv}
ACCESS_TOKEN="$(${lib.getExe' fetchAccessToken "github-app-access-token"})"
export ACCESS_TOKEN
${lib.getExe' removeRunner "github-runner-remove"} || true
'';
in
map (
x:
"${x} ${
lib.escapeShellArgs [
stateDir
workDir
logsDir
]
}"
) [ "-+${unregister}" ] # runs as root
);
# If running in ephemeral mode, restart the service on-exit (i.e., successful de-registration of the runner)
# to trigger a fresh registration.
Restart = if cfg.ephemeral then "on-success" else "no";
@@ -401,12 +256,11 @@
WorkingDirectory = workDir;
InaccessiblePaths = [
# Token file path given in the configuration, if visible to the service
"-${cfg.tokenFile}"
# Token file in the state directory
"${stateDir}/${currentConfigTokenFilename}"
]
# Token file path given in the configuration, if visible to the service
++ lib.optional (cfg.tokenFile != null) "-${cfg.tokenFile}"
++ lib.optional (cfg.githubApp != null) "-${cfg.githubApp.privateKeyFile}";
];
KillSignal = "SIGINT";

View File

@@ -1,279 +0,0 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.kvrocks;
format = pkgs.formats.keyValue {
# Emit list values as repeated keys (e.g. rename-command), matching MultiStringField.
listsAsDuplicateKeys = true;
mkKeyValue = lib.generators.mkKeyValueDefault {
mkValueString = v: if lib.isBool v then lib.boolToYesNo v else toString v;
} " ";
};
defaultDir = "/var/lib/kvrocks";
dataDir = cfg.settings.dir;
isDefaultDir = dataDir == defaultDir;
# Defaults match upstream Config field defaults (config.cc).
# freeformType uses listsAsDuplicateKeys, so set values are singleton lists.
workers = lib.head (cfg.settings.workers or [ 8 ]);
maxBackgroundJobs = lib.head (cfg.settings."rocksdb.max_background_jobs" or [ 4 ]);
maxclients = lib.head (cfg.settings.maxclients or [ 10240 ]);
maxOpenFiles = lib.head (cfg.settings."rocksdb.max_open_files" or [ 8096 ]);
# Thread inventory from server.cc ("Kvrocks threads list") + Server::Start:
# always-on: main, workers, task-runner (1), server-cron, compact-check,
# rocksdb background (bounded by max_background_jobs)
# optional: master-repl (+ ≤4 parallel fetch via std::async on full sync),
# feed-slave per replica, slot-migrate (cluster)
alwaysOnThreads = 1 + workers + 1 + 1 + 1 + maxBackgroundJobs;
# 1 master-repl + 4 fetch + 1 slot-migrate + ~16 replicas + misc (jemalloc, …)
dynamicThreadMargin = 32;
# From Server::AdjustOpenFilesLimit:
# max_files = maxclients + rocksdb.max_open_files + min_reserved_fds
# min_reserved_fds = 128 (listen sockets, logs, persistence, misc)
openFilesReserved = 128;
hasUnixSocket = cfg.settings.unixsocket != "";
hasTcp = lib.length cfg.settings.bind > 0;
configFile = format.generate "kvrocks.conf" (
{
daemonize = "no";
supervised = "systemd";
}
// (builtins.removeAttrs cfg.settings [
"bind"
"unixsocket"
])
// lib.optionalAttrs (hasTcp && !cfg.socketActivation) {
bind = lib.concatStringsSep " " cfg.settings.bind;
}
// lib.optionalAttrs hasUnixSocket {
unixsocket = cfg.settings.unixsocket;
}
// lib.optionalAttrs cfg.socketActivation {
socket-fd = 3;
}
);
in
{
meta.maintainers = pkgs.kvrocks.meta.maintainers;
options = {
services.kvrocks = {
enable = lib.mkEnableOption "the Kvrocks server";
package = lib.mkPackageOption pkgs "kvrocks" { };
user = lib.mkOption {
type = lib.types.str;
default = "kvrocks";
description = "User account under which Kvrocks runs.";
};
group = lib.mkOption {
type = lib.types.str;
default = "kvrocks";
description = "Group under which Kvrocks runs.";
};
socketActivation = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Enable systemd socket activation for TCP.
Requires exactly one address in {option}`services.kvrocks.settings.bind`.
'';
};
settings = lib.mkOption {
type = lib.types.submodule {
freeformType = format.type;
options = {
bind = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [
"127.0.0.1"
"::1"
];
description = "The addresses to bind to.";
};
port = lib.mkOption {
type = lib.types.port;
default = 6666;
description = "Accept connections on the specified port.";
};
unixsocket = lib.mkOption {
type = lib.types.str;
default = "";
example = "/run/kvrocks/kvrocks.sock";
description = "Unix socket path.";
};
dir = lib.mkOption {
type = lib.types.str;
default = defaultDir;
description = "Directory for database files.";
};
};
};
default = { };
example = {
workers = 8;
maxclients = 10000;
rename-command = [
"KEYS \"\""
"FLUSHDB \"\""
];
};
description = ''
Configuration for kvrocks.
See <https://github.com/apache/kvrocks/blob/unstable/kvrocks.conf> for supported options.
List values are emitted as repeated keys (for example `rename-command`),
except {option}`services.kvrocks.settings.bind` which is space-separated
on a single line.
'';
};
openFirewall = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to open the firewall for the kvrocks port.";
};
};
};
config = lib.mkIf cfg.enable {
assertions = [
{
assertion = hasTcp || hasUnixSocket;
message = "services.kvrocks: set settings.bind and/or settings.unixsocket.";
}
{
assertion = cfg.socketActivation -> builtins.length cfg.settings.bind == 1;
message = "services.kvrocks.socketActivation requires exactly one settings.bind address.";
}
];
networking.firewall.allowedTCPPorts = lib.mkIf (cfg.openFirewall && hasTcp) [
cfg.settings.port
];
systemd.tmpfiles.settings."10-kvrocks" = lib.mkIf (!isDefaultDir) {
${dataDir}.d = {
user = cfg.user;
group = cfg.group;
mode = "0700";
};
};
systemd.sockets.kvrocks = lib.mkIf cfg.socketActivation {
description = "Kvrocks socket";
wantedBy = [ "sockets.target" ];
listenStreams =
let
addr = builtins.head cfg.settings.bind;
port = toString cfg.settings.port;
listenStream = if lib.hasInfix ":" addr then "[${addr}]:${port}" else "${addr}:${port}";
in
lib.singleton listenStream;
socketConfig = {
Accept = false;
SocketUser = cfg.user;
SocketGroup = cfg.group;
};
};
systemd.services.kvrocks = {
description = "Kvrocks - Distributed key value database";
documentation = [ "https://kvrocks.apache.org/" ];
wantedBy = lib.mkIf (!cfg.socketActivation) [ "multi-user.target" ];
after = [ "network.target" ] ++ lib.optionals cfg.socketActivation [ "kvrocks.socket" ];
requires = lib.optionals cfg.socketActivation [ "kvrocks.socket" ];
serviceConfig = {
Type = "notify";
ExecStart = "${lib.getExe cfg.package} -c ${configFile}";
Restart = "on-failure";
RestartSec = "10s";
User = cfg.user;
Group = cfg.group;
StateDirectory = lib.mkIf isDefaultDir "kvrocks";
StateDirectoryMode = "0700";
RuntimeDirectory = "kvrocks";
RuntimeDirectoryMode = "0755";
BindPaths = lib.mkIf (!isDefaultDir) [ dataDir ];
LimitNPROC = lib.mkDefault (alwaysOnThreads + dynamicThreadMargin);
# When rocksdb.max_open_files is -1 (unlimited), fall back to a high limit.
LimitNOFILE = lib.mkDefault (
if maxOpenFiles < 0 then 1048576 else maxclients + maxOpenFiles + openFilesReserved
);
TimeoutSec = 300;
NonBlocking = lib.mkIf cfg.socketActivation true;
# Capabilities
CapabilityBoundingSet = "";
# Security
NoNewPrivileges = true;
# Sandboxing
TemporaryFileSystem = [ "/:ro" ];
BindReadOnlyPaths = [
builtins.storeDir
"/etc"
];
ProtectSystem = "strict";
ProtectHome = true;
PrivateTmp = true;
PrivateDevices = true;
PrivateUsers = true;
ProtectClock = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectControlGroups = true;
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
RestrictNamespaces = true;
LockPersonality = true;
MemoryDenyWriteExecute = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
PrivateMounts = true;
SocketBindDeny = [ "any" ];
SocketBindAllow = lib.optionals (hasTcp && !cfg.socketActivation) [
"tcp:${toString cfg.settings.port}"
];
# System Call Filtering
SystemCallArchitectures = "native";
SystemCallFilter = "~@cpu-emulation @debug @keyring @memlock @mount @obsolete @privileged @resources @setuid";
};
};
users = {
users = lib.mkIf (cfg.user == "kvrocks") {
kvrocks = {
isSystemUser = true;
group = cfg.group;
description = "Kvrocks daemon user";
};
};
groups = lib.mkIf (cfg.group == "kvrocks") {
kvrocks = { };
};
};
};
}

View File

@@ -752,7 +752,7 @@ in
"AF_INET6"
];
LockPersonality = true;
MemoryDenyWriteExecute = false; # pcre2 jit
MemoryDenyWriteExecute = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
PrivateMounts = true;

View File

@@ -400,15 +400,6 @@ in
pkgs.xdg-user-dirs-gtk # Used to create the default bookmarks
];
# Restarting this unit terminates the active GNOME session.
systemd.user.services.gnome-session-monitor = {
restartIfChanged = false;
overrideStrategy = "asDropin";
# No need to add the NixOS default Environment="Path=coreutils:...",
# to the gnome-session-monitor service.
enableDefaultPath = false;
};
services.udev.packages = [
# Force enable KMS modifiers for devices that require them.
# https://gitlab.gnome.org/GNOME/pkgs.mutter/-/merge_requests/1443

View File

@@ -22,9 +22,6 @@ let
// lib.optionalAttrs (cfg.ipcPasswordFile != null) {
IPCPassword = "#ipcPassword#";
}
// lib.optionalAttrs (cfg.licenseFile != null) {
LicenseID = "#licenseID#";
}
);
ipc-config = format.generate "IPC.config" cfg.ipcSettings;
@@ -112,12 +109,6 @@ in
default = { };
};
licenseFile = lib.mkOption {
type = with lib.types; nullOr path;
default = null;
description = "Path to a file containing the license. The file must be readable by the `archisteamfarm` user/group.";
};
ipcPasswordFile = lib.mkOption {
type = with lib.types; nullOr path;
default = null;
@@ -282,10 +273,6 @@ in
cp --no-preserve=mode ${configFile} config/ASF.json
${lib.optionalString (cfg.licenseFile != null) ''
${replaceSecretBin} '#licenseID#' '${cfg.licenseFile}' config/ASF.json
''}
${lib.optionalString (cfg.ipcPasswordFile != null) ''
${replaceSecretBin} '#ipcPassword#' '${cfg.ipcPasswordFile}' config/ASF.json
''}

View File

@@ -1,134 +0,0 @@
{
lib,
config,
pkgs,
...
}:
let
inherit (lib)
mkEnableOption
mkIf
mkOption
types
mkPackageOption
getExe'
;
cfg = config.services.cardwired;
tomlFormat = pkgs.formats.toml { };
in
{
options.services.cardwired = {
enable = mkEnableOption "Cardwire eBPF-based GPU manager daemon";
package = mkPackageOption pkgs "cardwire" { };
settings = mkOption {
type = types.submodule {
options = {
auto_apply_gpu_state = mkOption {
type = types.bool;
default = true;
description = ''
Automatically restore GPU states on manual mode.
'';
};
experimental_nvidia_block = mkOption {
type = types.bool;
default = false;
description = ''
Enable blocking specifics Nvidia files. This setting is experimental
because these files can be shared across multiple Nvidia GPUs.
'';
};
battery_auto_switch = mkOption {
type = types.bool;
default = false;
description = ''
Automatically switch mode on AC power.
'';
};
battery_auto_switch_mode = mkOption {
type = types.enum [
"integrated"
"hybrid"
"manual"
"smart"
];
default = "hybrid";
description = ''
The mode cardwire switches on AC power.
'';
};
external_display_auto_switch = mkOption {
type = types.bool;
default = false;
description = ''
Automatically make GPUs available for displays connected to dGPU-only ports.
'';
};
};
};
default = { };
description = ''
Configuration for {file}`/etc/cardwire.toml`
See <https://opengamingcollective.github.io/cardwire/getting-started/usage>
'';
};
};
config = mkIf cfg.enable {
environment.etc."cardwire/cardwire.toml".source = tomlFormat.generate "cardwire.toml" cfg.settings;
services.dbus.enable = true;
services.dbus.packages = [ cfg.package ];
environment.systemPackages = [ cfg.package ];
systemd.services.cardwired = {
description = "Cardwire Daemon";
restartTriggers = [ config.environment.etc."cardwire/cardwire.toml".source ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "dbus";
BusName = "org.opengamingcollective.cardwire";
ExecStart = getExe' cfg.package "cardwired";
Restart = "on-failure";
RestartSec = "5s";
User = "root";
PrivateNetwork = true;
PrivateTmp = true;
ProtectHostname = true;
NoNewPrivileges = true;
ProtectClock = true;
ProtectSystem = "strict";
StateDirectory = "cardwire";
StateDirectoryMode = "0700";
ConfigurationDirectory = "cardwire";
ConfigurationDirectoryMode = "0700";
ProtectHome = "read-only";
ProtectKernelLogs = true;
ProtectControlGroups = true;
ProtectKernelModules = true;
RestrictAddressFamilies = [
"AF_UNIX"
"AF_NETLINK"
];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
LockPersonality = true;
UMask = "0077";
IPAddressDeny = "any";
CapabilityBoundingSet = [
"CAP_SYS_ADMIN"
"CAP_BPF"
"CAP_SYS_PTRACE"
"CAP_DAC_OVERRIDE"
];
};
};
};
}

View File

@@ -31,11 +31,8 @@ let
originalEtc =
let
mkEtcFile = n: lib.nameValuePair n { source = "${cfg.package}/etc/${n}"; };
etcFiles = lib.filter (
f: config.boot.loader.grub.enable || f != "grub.d/35_fwupd"
) cfg.package.filesInstalledToEtc;
in
lib.listToAttrs (map mkEtcFile etcFiles);
lib.listToAttrs (map mkEtcFile cfg.package.filesInstalledToEtc);
extraTrustedKeys =
let
mkName = p: "pki/fwupd/${baseNameOf p}";
@@ -187,7 +184,7 @@ in
config = lib.mkIf cfg.enable {
# Disable test related plug-ins implicitly so that users do not have to care about them.
services.fwupd.daemonSettings = {
EspLocation = lib.mkDefault config.boot.loader.efi.efiSysMountPoint;
EspLocation = config.boot.loader.efi.efiSysMountPoint;
};
environment.systemPackages = [ cfg.package ];
@@ -205,12 +202,6 @@ in
systemd = {
packages = [ cfg.package ];
# fwupd looks for its EFI app in /run/fwupd-efi so that signed variants can
# be placed next to it; `C+` keeps those signed files across a rebuild.
tmpfiles.rules = [
"C+ /run/fwupd-efi - - - - ${cfg.package.fwupd-efi}/libexec/fwupd/efi"
];
# The upstream unit runs as User=fwupd-refresh; ensure it can take
# ownership of /var/lib/fwupd.
services.fwupd-refresh.serviceConfig = {

View File

@@ -181,9 +181,6 @@ in
# ot-ctl can be used to query the router instance
environment.systemPackages = [ cfg.package ];
# Shared by the agent and web interface for the OpenThread control socket.
users.groups.otbr = { };
# Make sure we have ipv6 support, and that forwarding is enabled
networking.enableIPv6 = true;
networking.firewall.allowedTCPPorts =
@@ -220,7 +217,6 @@ in
THREAD_IF = cfg.interfaceName;
};
serviceConfig = {
Group = "otbr";
ExecStartPre = "${utils.escapeSystemdExecArg (lib.getExe' cfg.package "otbr-firewall")} start";
ExecStart = lib.concatStringsSep " " (
lib.concatLists [
@@ -273,7 +269,7 @@ in
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
UMask = "0007";
UMask = "0077";
CapabilityBoundingSet = [
"CAP_NET_ADMIN"
@@ -292,7 +288,6 @@ in
after = [ "otbr-agent.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Group = "otbr";
ExecStart = lib.concatStringsSep " " (
lib.concatLists [
[

View File

@@ -10,8 +10,6 @@ let
cfg = config.services.wyoming.faster-whisper;
inherit (lib)
any
hasInfix
mapAttrsToList
mkOption
mkEnableOption
@@ -71,7 +69,6 @@ in
"auto"
"faster-whisper"
"onnx-asr"
"qwen3-asr"
"sherpa"
"transformers"
];
@@ -314,13 +311,8 @@ in
in
mkIf (cfg.servers != { }) {
assertions = mapAttrsToList (server: options: {
assertion =
!lib.elem options.sttLibrary [
"faster-whisper"
"qwen3-asr"
]
-> options.initialPrompt == null;
message = "wyoming-faster-whisper/${server}: Initial prompt is only supported when `sttLibrary` is `faster-whisper` or `qwen3-asr`.";
assertion = options.sttLibrary != "faster-whisper" -> options.initialPrompt == null;
message = "wyoming-faster-whisper/${server}: Initial prompt is only supported when using `faster-whisper` as `sttLibrary`.";
}) cfg.servers;
systemd.services = mapAttrs' (
@@ -329,9 +321,6 @@ in
finalPackage = cfg.package.overridePythonAttrs (oldAttrs: {
dependencies =
oldAttrs.dependencies
++ optionals (any (
arg: hasInfix "--hass" arg
) options.extraArgs) oldAttrs.optional-dependencies.hass
++ optionals options.zeroconf.enable oldAttrs.optional-dependencies.zeroconf
++ optionals (
options.sttLibrary == "onnx-asr" || options.sttLibrary == "auto" && options.language == "ru"

View File

@@ -355,7 +355,7 @@ in
PrivateDevices = true;
ProtectSystem = "full";
CapabilityBoundingSet = [ "~CAP_NET_ADMIN CAP_SYS_ADMIN CAP_SYS_BOOT CAP_SYS_MODULE" ];
MemoryDenyWriteExecute = false; # pcre2 jit
MemoryDenyWriteExecute = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectControlGroups = true;

View File

@@ -1061,7 +1061,7 @@ in
"CAP_SYS_RESOURCE"
];
LockPersonality = true;
MemoryDenyWriteExecute = false; # pcre2 jit
MemoryDenyWriteExecute = true;
NoNewPrivileges = false; # e.g for sendmail
OOMPolicy = "continue";
PrivateTmp = true;

View File

@@ -1011,7 +1011,7 @@ in
PrivateDevices = true;
ProtectSystem = "full";
CapabilityBoundingSet = [ "~CAP_NET_ADMIN CAP_SYS_ADMIN CAP_SYS_BOOT CAP_SYS_MODULE" ];
MemoryDenyWriteExecute = false; # pcre2 jit
MemoryDenyWriteExecute = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectControlGroups = true;

View File

@@ -6,9 +6,6 @@
}:
let
cfg = config.services.comfyui;
# By default a StateDirectory is used; anything else needs its own directory and a hole in the unit's mount namespace.
isDefaultDataDir = cfg.dataDir == "/var/lib/comfyui";
in
{
options = {
@@ -16,20 +13,6 @@ in
enable = lib.mkEnableOption "ComfyUI";
package = lib.mkPackageOption pkgs "comfyui" { };
dataDir = lib.mkOption {
type = lib.types.path;
default = "/var/lib/comfyui";
example = "/srv/comfyui";
description = ''
Directory holding ComfyUI's state: models, custom nodes, inputs, outputs and the database.
Defaults to the unit's `StateDirectory`.
Any other directory is created with systemd-tmpfiles and bind-mounted into the service.
Existing state is not migrated, you need to move it yourself.
'';
};
listen = lib.mkOption {
type = with lib.types; listOf str;
default = [
@@ -63,30 +46,19 @@ in
];
description = ''
Extra arguments to pass to the server. See `comfyui --help` for available args.
Flags set by the module are prepended with `lib.mkBefore`, so any user supplied flag wins over that.
'';
};
};
};
config = lib.mkIf cfg.enable {
services.comfyui.extraArgs = lib.mkBefore [
"--base-directory=${cfg.dataDir}"
"--database-url=sqlite:///${cfg.dataDir}/user/comfyui.db"
services.comfyui.extraArgs = [
"--base-directory=/var/lib/comfyui"
"--database-url=sqlite:////var/lib/comfyui/user/comfyui.db"
"--listen=${lib.concatStringsSep "," cfg.listen}"
"--port=${toString cfg.port}"
];
# owner read back from the unit, not spelled out: StateDirectory= infers it, this rule has to be told
systemd.tmpfiles.settings = lib.mkIf (!isDefaultDataDir) {
"10-comfyui".${cfg.dataDir}.d = {
user = config.systemd.services.comfyui.serviceConfig.User;
group = config.systemd.services.comfyui.serviceConfig.Group;
mode = "0700";
};
};
systemd.services.comfyui = {
description = "Powerful and modular diffusion model GUI, api and backend";
wantedBy = [ "multi-user.target" ];
@@ -94,8 +66,8 @@ in
preStart = ''
for d in custom_nodes input output models; do
if [[ ! -d "${cfg.dataDir}/$d" ]]; then
cp --no-preserve=all -r ${cfg.package}/share/comfyui/$d "${cfg.dataDir}/"
if [[ ! -d /var/lib/comfyui/$d ]]; then
cp --no-preserve=all -r ${cfg.package}/share/comfyui/$d /var/lib/comfyui/
fi
done
'';
@@ -105,13 +77,10 @@ in
Group = "comfyui";
Restart = "always";
RestartSec = "5sec"; # don't crash loop immediately
StateDirectory = lib.mkIf isDefaultDataDir "comfyui";
StateDirectory = "comfyui";
Type = "simple";
User = "comfyui";
# This is a bind mount, not ReadWritePaths, so that with ProtectHome it is not shadowed by a tmpfs.
BindPaths = lib.mkIf (!isDefaultDataDir) [ cfg.dataDir ];
# required for Torch and GPU acceleration
BindReadOnlyPaths = [ "/proc/cpuinfo" ];
PrivateDevices = false;
@@ -154,7 +123,7 @@ in
groups.comfyui = { };
users.comfyui = {
group = "comfyui";
home = cfg.dataDir;
home = "/var/lib/comfyui";
isSystemUser = true;
};
};

View File

@@ -777,7 +777,7 @@ in
];
RestrictNamespaces = true;
LockPersonality = true;
MemoryDenyWriteExecute = false; # pcre2 jit
MemoryDenyWriteExecute = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
RemoveIPC = true;

View File

@@ -953,7 +953,7 @@ in
++ lib.optional (useSendmail && config.services.postfix.enable) "AF_NETLINK";
RestrictNamespaces = true;
LockPersonality = true;
MemoryDenyWriteExecute = false; # pcre2 jit
MemoryDenyWriteExecute = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
RemoveIPC = true;

View File

@@ -1208,8 +1208,6 @@ in
services.postgresql = optionalAttrs databaseActuallyCreateLocally {
enable = true;
ensureUsers = singleton { name = cfg.databaseUsername; };
# With the PostgreSQL update to 17.6, the default was no longer enough.
settings.max_locks_per_transaction = lib.mkDefault 128;
};
# Enable rotation of log files
@@ -1307,7 +1305,6 @@ in
"d ${cfg.statePath}/custom_hooks/post-receive.d 0700 ${cfg.user} ${cfg.group} -"
"d ${cfg.statePath}/custom_hooks/update.d 0700 ${cfg.user} ${cfg.group} -"
"d ${gitlabConfig.production.shared.path} 0750 ${cfg.user} ${cfg.group} -"
"d ${gitlabConfig.production.shared.path}/agent_plan_content 0750 ${cfg.user} ${cfg.group} -"
"d ${gitlabConfig.production.shared.path}/artifacts 0750 ${cfg.user} ${cfg.group} -"
"d ${gitlabConfig.production.shared.path}/lfs-objects 0750 ${cfg.user} ${cfg.group} -"
"d ${gitlabConfig.production.shared.path}/packages 0750 ${cfg.user} ${cfg.group} -"

View File

@@ -70,9 +70,7 @@ let
if [[ "$USER" != ${cfg.user} ]]; then
${
if config.security.sudo.enable then
"sudo='exec ${config.security.wrapperDir}/sudo -u ${cfg.user} -g ${cfg.group} ${
lib.optionalString enableRedis " -g " + redisServer.group
} -E'"
"sudo='exec ${config.security.wrapperDir}/sudo -u ${cfg.user} -E'"
else
">&2 echo 'Aborting, paperless-manage must be run as user `${cfg.user}`!'; exit 2"
}
@@ -123,7 +121,7 @@ let
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SupplementaryGroups = lib.optional enableRedis redisServer.group;
SupplementaryGroups = lib.optional enableRedis redisServer.user;
SystemCallArchitectures = "native";
SystemCallFilter = [
"@system-service"
@@ -272,29 +270,7 @@ in
user = lib.mkOption {
type = lib.types.str;
default = defaultUser;
description = ''
User under which Paperless runs
::: {.note}
If left as the default value this user will automatically be
created on system activation, otherwise you are responsible for
ensuring the group exists before the paperless service starts.
:::
'';
};
group = lib.mkOption {
type = lib.types.str;
default = defaultUser;
description = ''
Primary group under which Paperless runs
::: {.note}
If left as the default value this group will automatically be
created on system activation, otherwise you are responsible for
ensuring the group exists before the redis service starts.
:::
'';
description = "User under which Paperless runs.";
};
package = lib.mkPackageOption pkgs "paperless-ngx" { } // {
@@ -598,7 +574,7 @@ in
# and automatically migrates when needed (e.g. with v2 -> v3 swapping from Whoosh to Tantivy)
${lib.getExe cfg.package} document_index reindex --if-needed --no-progress-bar
if ${lib.boolToString (cfg.passwordFile != null)} || [[ -n ''${PAPERLESS_ADMIN_PASSWORD-} ]]; then
if ${lib.boolToString (cfg.passwordFile != null)} || [[ -n $PAPERLESS_ADMIN_PASSWORD ]]; then
export PAPERLESS_ADMIN_USER="''${PAPERLESS_ADMIN_USER:-admin}"
if [[ -e $CREDENTIALS_DIRECTORY/PAPERLESS_ADMIN_PASSWORD ]]; then
PAPERLESS_ADMIN_PASSWORD=$(cat "$CREDENTIALS_DIRECTORY/PAPERLESS_ADMIN_PASSWORD")

View File

@@ -137,7 +137,6 @@ in
services.${systemdName} = {
inherit description;
path = [ config.programs.ssh.package ];
serviceConfig = {
Type = "oneshot";

View File

@@ -2,14 +2,13 @@
config,
pkgs,
lib,
utils,
...
}:
let
cfg = config.services.seerr;
# 26.05 introduced a breaking change which is guarded behind stateRevision to
# avoid breaking users.
useNewConfigLocation = cfg.stateRevision >= 1;
# 26.05 introduced a breaking change which is guarded behind stateVersion to avoid
# breaking users.
useNewConfigLocation = lib.versionAtLeast config.system.stateVersion "26.05";
in
{
imports = [
@@ -40,23 +39,8 @@ in
configDir = lib.mkOption {
type = lib.types.path;
default = if useNewConfigLocation then "/var/lib/seerr/" else "/var/lib/jellyseerr/config";
defaultText = lib.literalMD "{file}`/var/lib/seerr` (or {file}`/var/lib/jellyseerr/config` if {option}`services.seerr.stateRevision` < 1)";
description = "Config data directory";
};
stateRevision = utils.mkStateRevisionOption {
descriptionName = "Seerr";
migrations = {
"26.05" = ''
Move {file}`/var/lib/private/jellyseerr/config` to
{file}`/var/lib/private/seerr`, if you have not set
{option}`services.seerr.configDir`. (If you have set
{option}`services.seerr.configDir`, you should also have forced
{option}`systemd.services.seerr.serviceConfig.StateDirectory`, and in
that case `stateRevision` does not affect your configuration.)
'';
};
};
};
config = lib.mkIf cfg.enable {
@@ -97,7 +81,5 @@ in
networking.firewall = lib.mkIf cfg.openFirewall {
allowedTCPPorts = [ cfg.port ];
};
system.moduleStateRevisions."services.seerr.stateRevision" = cfg.stateRevision;
};
}

View File

@@ -13,7 +13,6 @@ let
ln -s /run/wrappers/bin/cgroup-network $out/libexec/netdata/plugins.d/cgroup-network
ln -s /run/wrappers/bin/debugfs.plugin $out/libexec/netdata/plugins.d/debugfs.plugin
ln -s /run/wrappers/bin/freeipmi.plugin $out/libexec/netdata/plugins.d/freeipmi.plugin
ln -s /run/wrappers/bin/go.d.plugin $out/libexec/netdata/plugins.d/go.d.plugin
ln -s /run/wrappers/bin/logs-management.plugin $out/libexec/netdata/plugins.d/logs-management.plugin
ln -s /run/wrappers/bin/network-viewer.plugin $out/libexec/netdata/plugins.d/network-viewer.plugin
ln -s /run/wrappers/bin/otel-plugin $out/libexec/netdata/plugins.d/otel-plugin
@@ -169,7 +168,7 @@ in
`cfg.package` must be built with `withNdsudo = true`
:::
'';
example = lib.literalExpression ''
example = ''
[
pkgs.smartmontools
pkgs.nvme-cli
@@ -370,10 +369,8 @@ in
# Capabilities
CapabilityBoundingSet = [
"CAP_DAC_OVERRIDE" # is required for freeipmi and slabinfo plugins
"CAP_DAC_READ_SEARCH" # is required for apps, systemd-journal and go.d/filecheck collector
"CAP_NET_ADMIN" # is required for the go.d/wireguard collector
"CAP_NET_BIND_SERVICE" # is required for the go.d/snmp_traps collector
"CAP_NET_RAW" # is required for the go.d/ping collector
"CAP_DAC_READ_SEARCH" # is required for apps and systemd-journal plugin
"CAP_NET_RAW" # is required for fping app
"CAP_PERFMON" # is required for perf plugin
"CAP_SETPCAP" # is required for apps, perf and slabinfo plugins
"CAP_SETUID" # is required for cgroups and cgroups-network plugins
@@ -387,10 +384,7 @@ in
"CAP_FOWNER"
"CAP_SYS_RAWIO"
]
++ lib.optionals cfg.package.withNdsudo [
"CAP_SETGID" # is required for ndsudo to fully acquire root privileges (setgid/setegid, alongside CAP_SETUID above)
"CAP_SYS_RAWIO" # is required for ndsudo to run S.M.A.R.T./NVMe raw-IO commands (e.g. smartctl, nvme-cli)
];
++ lib.optional isThereAnyWireGuardTunnels "CAP_NET_ADMIN";
# Sandboxing
ProtectSystem = "full";
ProtectHome = "read-only";
@@ -436,22 +430,6 @@ in
permissions = "u+rx,g+x,o-rwx";
};
"go.d.plugin" = {
source = "${cfg.package}/libexec/netdata/plugins.d/godplugin";
# https://github.com/netdata/netdata/blob/master/src/go/plugin/go.d/README.md#required-linux-capabilities
capabilities =
lib.concatStringsSep "," [
"cap_dac_read_search" # is required for the go.d/filecheck collector
"cap_net_admin" # is required for the go.d/wireguard collector
"cap_net_bind_service" # is required for the go.d/snmp_traps collector
"cap_net_raw" # is required for the go.d/ping collector
]
+ "+eip";
owner = cfg.user;
group = cfg.group;
permissions = "u+rx,g+x,o-rwx";
};
"cgroup-network" = {
source = "${cfg.package}/libexec/netdata/plugins.d/cgroup-network.org";
capabilities = "cap_setuid+ep";

View File

@@ -82,7 +82,6 @@ let
"keylight"
"klipper"
"knot"
"kvrocks"
"libvirt"
"lnd"
"mail"

View File

@@ -1,19 +0,0 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.prometheus.exporters.kvrocks;
in
{
port = 9121;
serviceOpts = {
serviceConfig = {
RestrictAddressFamilies = [ "AF_UNIX" ];
ExecStart = "${lib.getExe pkgs.prometheus-kvrocks-exporter} -web.listen-address ${cfg.listenAddress}:${toString cfg.port} ${lib.escapeShellArgs cfg.extraFlags}";
};
};
}

View File

@@ -679,7 +679,7 @@ in
environment = envVars;
serviceConfig = {
Type = "oneshot";
ExecStart = "${cfg.package}/bin/upsdrvctl -u root shutdown";
ExecStart = "${cfg.package}/bin/upsdrvctl shutdown";
};
};

View File

@@ -73,7 +73,6 @@ in
PrivateMounts = true;
PrivateTmp = true;
PrivateUsers = true;
ProcSubset = "pid";
ProtectClock = true;
ProtectControlGroups = "strict";
ProtectHome = true;

View File

@@ -436,15 +436,6 @@ in
"@system-service"
"~@privileged"
"@chown setgroups setresuid"
]
++ lib.optionals pkgs.stdenv.hostPlatform.is32bit [
# glibc's setresuid()/setgroups() invoke the kernel's 32-bit compat
# syscalls (setresuid32/setgroups32) on 32-bit architectures --
# distinct syscalls from the ones already allowlisted above, so
# without these 2, avahi-daemon is killed with SIGSYS as soon as it
# tries to drop privileges.
"setgroups32"
"setresuid32"
];
UMask = "0077";
};

View File

@@ -8,7 +8,6 @@
let
cfg = config.services.dnscrypt-proxy;
settingsFormat = pkgs.formats.toml { };
in
@@ -37,7 +36,7 @@ in
};
}
'';
type = settingsFormat.type;
type = lib.types.attrs;
default = { };
};

View File

@@ -53,14 +53,6 @@ in
The maximum log size.
'';
};
telemetry = lib.mkOption {
default = false;
type = lib.types.bool;
description = ''
Collects anonymous usage data and crash reports.
'';
};
};
};
@@ -79,8 +71,8 @@ in
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
Type = "exec";
ExecStart = "${pkgs.eternal-terminal}/bin/etserver --logtostdout --cfgfile=${pkgs.writeText "et.cfg" ''
Type = "forking";
ExecStart = "${pkgs.eternal-terminal}/bin/etserver --daemon --cfgfile=${pkgs.writeText "et.cfg" ''
; et.cfg : Config file for Eternal Terminal
;
@@ -91,15 +83,15 @@ in
verbose = ${toString cfg.verbosity}
silent = ${if cfg.silent then "1" else "0"}
logsize = ${toString cfg.logSize}
telemetry = ${lib.boolToString cfg.telemetry}
''}";
Restart = "on-failure";
KillMode = "process";
};
};
};
};
meta = {
maintainers = with lib.maintainers; [ tomasrivera ];
maintainers = [ ];
};
}

View File

@@ -28,7 +28,7 @@ let
ProtectSystem = "full";
ProtectHome = "on";
Restart = "on-failure";
RestartSec = 1;
RestartSec = 2;
LogsDirectory = "radius";
};
};

View File

@@ -29,8 +29,10 @@ in
};
description = ''
Configuration for GoDNS. Refer to the [configuration section](https://github.com/TimothyYe/godns?tab=readme-ov-file#configuration) in the
Configuration for GoDNS. Refer to the [configuration section](1) in the
GoDNS GitHub repository for details.
[1]: https://github.com/TimothyYe/godns?tab=readme-ov-file#configuration
'';
example = {

View File

@@ -795,7 +795,7 @@ in
- {var}`"wpa2-sha256"`: WPA2-Personal using HMAC-SHA256 (IEEE 802.11i/RSN). Passwords are set
using {option}`wpaPassword` or preferably by {option}`wpaPasswordFile` or {option}`wpaPskFile`.
- {var}`"wpa3-sae-transition"`: Use WPA3-Personal (SAE) if possible, otherwise fallback
to WPA2-Personal. Only use if necessary and switch to the newer WPA3-SAE when possible.
to WPA2-SHA256. Only use if necessary and switch to the newer WPA3-SAE when possible.
You will have to specify both {option}`wpaPassword` and {option}`saePasswords` (or one of their alternatives).
- {var}`"wpa3-sae"`: Use WPA3-Personal (SAE). This is currently the recommended way to
setup a secured WiFi AP (as of March 2023) and therefore the default. Passwords are set
@@ -803,27 +803,6 @@ in
'';
};
transitionDisable = mkOption {
default = false;
example = true;
type = types.bool;
description = ''
Enable the WPA3-Personal Transition Disable indication. After a successful
WPA3 association, compatible stations will disable WPA2-Personal for this
network profile, protecting subsequent associations against downgrade attacks.
Legacy stations can still connect using WPA2 in transition mode.
This option is supported in `"wpa3-sae"` and `"wpa3-sae-transition"` modes.
::: {.warning}
Enable this only when every BSS in the network using this SSID supports
WPA3-Personal. The indication applies to a station's entire network profile
and can prevent it from connecting to WPA2-only BSSs. Therefore, the WPA3 spec
requires this to be disabled by default.
:::
'';
};
pairwiseCiphers = mkOption {
default = [ "CCMP" ];
example = [
@@ -1096,12 +1075,15 @@ in
wpa_key_mgmt = "SAE";
# Derive PWE using both hunting-and-pecking loop and hash-to-element
sae_pwe = 2;
# Prevent downgrade attacks by indicating to clients that they should
# disable any transition modes from now on.
transition_disable = "0x01";
# Per WPA3 spec, MFP is required.
ieee80211w = 2;
}
// optionalAttrs (bssCfg.authentication.mode == "wpa3-sae-transition") {
wpa = 2;
wpa_key_mgmt = "WPA-PSK WPA-PSK-SHA256 SAE";
wpa_key_mgmt = "WPA-PSK-SHA256 SAE";
ieee80211w = 1;
}
// optionalAttrs (bssCfg.authentication.mode == "wpa2-sha1") {
@@ -1118,9 +1100,6 @@ in
wpa_pairwise = pairwiseCiphers;
rsn_pairwise = pairwiseCiphers;
}
// optionalAttrs bssCfg.authentication.transitionDisable {
transition_disable = "0x01";
}
// optionalAttrs (bssCfg.authentication.wpaPassword != null) {
wpa_passphrase = bssCfg.authentication.wpaPassword;
}
@@ -1432,15 +1411,6 @@ in
assertion = auth.saePasswords == [ ] || auth.saePasswordsFile == null;
message = "hostapd radio ${radio} bss ${bss}: must use only one SAE password option (saePasswords or saePasswordsFile)";
}
{
assertion =
auth.transitionDisable
-> builtins.elem auth.mode [
"wpa3-sae"
"wpa3-sae-transition"
];
message = "hostapd radio ${radio} bss ${bss}: transitionDisable requires WPA3-SAE or WPA3-SAE transition mode";
}
{
assertion = auth.mode == "wpa3-sae" -> (auth.saePasswords != [ ] || auth.saePasswordsFile != null);
message = "hostapd radio ${radio} bss ${bss}: uses WPA3-SAE which requires defining a sae password option";

File diff suppressed because it is too large Load Diff

View File

@@ -1,112 +0,0 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.krill;
toml = pkgs.formats.toml { };
in
{
meta = {
maintainers = [ lib.maintainers.stepbrobd ];
teams = [ lib.teams.ngi ];
};
options.services.krill = {
enable = lib.mkEnableOption "Krill, RPKI CA and Publication Server";
package = lib.mkPackageOption pkgs "krill" { };
environmentFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
example = "/run/secrets/krill";
description = ''
Environment file for krill.
Krill requires an admin token to start.
Use this file to provide `KRILL_ADMIN_TOKEN=...`
(e.g. via agenix or sops-nix).
'';
};
settings = lib.mkOption {
type = lib.types.submodule {
freeformType = toml.type;
options = {
storage_uri = lib.mkOption {
type = lib.types.str;
default = "/var/lib/krill/data";
description = ''
Where Krill stores its data e.g. CA keys and the publication repository.
The default keeps it inside service state directory.
'';
};
log_type = lib.mkOption {
type = lib.types.enum [
"stderr"
"file"
"syslog"
];
default = "stderr";
description = "Where Krill logs to.";
};
};
};
default = { };
description = ''
Configuration written to `krill.conf`.
See <https://krill.docs.nlnetlabs.nl/en/stable/config.html> for available options.
'';
};
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
systemd.services.krill = {
description = "Krill RPKI CA and Publication Server";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
Type = "exec";
ExecStart = "${lib.getExe' cfg.package "krill"} --config ${toml.generate "krill.conf" cfg.settings}";
EnvironmentFile = lib.optional (cfg.environmentFile != null) cfg.environmentFile;
Restart = "on-failure";
RestartSec = 10;
StateDirectory = "krill";
StateDirectoryMode = "0700";
RuntimeDirectory = "krill";
UMask = "0077";
CapabilityBoundingSet = [ "" ];
DynamicUser = true;
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateDevices = true;
PrivateTmp = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectSystem = "strict";
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
RestrictNamespaces = true;
RestrictRealtime = true;
SystemCallArchitectures = "native";
SystemCallErrorNumber = "EPERM";
SystemCallFilter = "@system-service";
};
};
};
}

View File

@@ -52,7 +52,6 @@ in
for possible values.
'';
type = types.submodule {
freeformType = yaml.type;
options.repo = {
scanPath = mkOption {
type = types.path;
@@ -61,12 +60,7 @@ in
};
readme = mkOption {
type = types.listOf types.str;
default = [
"readme"
"README"
"readme.md"
"README.md"
];
default = [ ];
description = "Readme files to look for.";
};
mainBranch = mkOption {
@@ -86,14 +80,14 @@ in
options.dirs = {
templates = mkOption {
type = types.path;
default = "${cfg.package}/lib/legit/templates";
defaultText = literalExpression ''"''${config.services.legit.package}/lib/legit/templates"'';
default = "${pkgs.legit-web}/lib/legit/templates";
defaultText = literalExpression ''"''${pkgs.legit-web}/lib/legit/templates"'';
description = "Directories where template files are located.";
};
static = mkOption {
type = types.path;
default = "${cfg.package}/lib/legit/static";
defaultText = literalExpression ''"''${config.services.legit.package}/lib/legit/static"'';
default = "${pkgs.legit-web}/lib/legit/static";
defaultText = literalExpression ''"''${pkgs.legit-web}/lib/legit/static"'';
description = "Directories where static files are located.";
};
};

View File

@@ -41,7 +41,7 @@ in
any_failed=0
addList() {
local payload="$1" result type id error
local payload="$1" result type error
echo "Adding list: $payload"
type=$($jq -r '.type' <<< "$payload")
@@ -49,25 +49,17 @@ in
error="$($jq '.error' <<< "$result")"
if [[ "$error" != "null" ]]; then
if $jq -e '
.error.key == "database_error"
and .error.hint == "The item is already present"
' <<< "$result" > /dev/null; then
echo "List already present"
echo "Error: $error"
any_failed=1
return
fi
echo "Error: $error"
any_failed=1
return
fi
id="$($jq '.lists.[].id?' <<< "$result")"
if [[ "$id" == "null" ]]; then
any_failed=1
error="$($jq '.processed.errors.[].error' <<< "$result")"
echo "Error: $error"
return
any_failed=1
error="$($jq '.processed.errors.[].error' <<< "$result")"
echo "Error: $error"
return
fi
echo "Added list ID $id: $result"

View File

@@ -144,9 +144,6 @@ in
"2270002"
"2270003"
"2270004"
"2270005"
"2270006"
"2270007"
];
description = ''
List of rules that should be disabled.
@@ -273,7 +270,7 @@ in
ProtectSystem = "strict";
DevicePolicy = "closed";
LockPersonality = true;
MemoryDenyWriteExecute = false; # pcre2 jit
MemoryDenyWriteExecute = true;
ProtectHostname = true;
ProtectProc = true;
ProtectKernelLogs = true;

View File

@@ -96,20 +96,6 @@ in
'';
};
importLegacyState = lib.mkOption {
type = lib.types.bool;
default = !cfg.static;
defaultText = lib.literalExpression "!config.services.userborn.static";
description = ''
Whether to include one-shot migration services that import the state
left behind by the perl activation script (`update-users-groups.pl`),
so id and subid allocations survive the switch to userborn.
Disable this if you want to keep the migration tooling out of your
system closure.
'';
};
};
config = lib.mkIf cfg.enable {
@@ -155,33 +141,6 @@ in
) userCfg.users
);
# One-shot import of update-users-groups.pl state. Runs before
# userborn so removed users' ids are reserved before allocation.
# Remove once the perl path has been gone for two releases.
services.userborn-import-legacy = lib.mkIf cfg.importLegacyState {
wantedBy = [ "sysinit.target" ];
requiredBy = [ "userborn.service" ];
before = [
"userborn.service"
"shutdown.target"
];
after = [ "systemd-remount-fs.service" ];
conflicts = [ "shutdown.target" ];
unitConfig = {
Description = "Import legacy update-users-groups.pl state for userborn";
DefaultDependencies = false;
ConditionPathExists = [
"/var/lib/nixos/uid-map"
"!/var/lib/userborn"
];
};
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = "${lib.getExe pkgs.userborn-import-legacy} --nogroup-gid ${toString config.ids.gids.nogroup} ${cfg.passwordFilesLocation}";
};
};
services.userborn = lib.mkIf (!cfg.static) {
wantedBy = [ "sysinit.target" ];
requiredBy = [ "sysinit-reactivation.target" ];

View File

@@ -28,19 +28,17 @@ let
gettyCmd = args: "${lib.getExe' pkgs.util-linux "agetty"} ${escapeShellArgs baseArgs} ${args}";
autologinOnceScript =
otherArgs:
pkgs.writeShellApplication {
name = "autologin-once";
text = ''
autologged="/run/agetty.autologged"
if test "$TTY" = tty1 && ! test -f "$autologged"; then
touch "$autologged"
exec ${gettyCmd "${otherArgs} --autologin ${cfg.autologinUser}"}
fi
exec ${gettyCmd otherArgs}
'';
};
autologinScript = ''
otherArgs="--noclear --keep-baud $TTY 115200,38400,9600 $TERM";
${lib.optionalString cfg.autologinOnce ''
autologged="/run/agetty.autologged"
if test "$TTY" = tty1 && ! test -f "$autologged"; then
touch "$autologged"
exec ${gettyCmd "$otherArgs --autologin ${cfg.autologinUser}"}
fi
''}
exec ${gettyCmd "$otherArgs"}
'';
in
@@ -181,12 +179,7 @@ in
serviceConfig.ExecStart = [
# override upstream default with an empty ExecStart
""
(
if cfg.autologinOnce then
lib.getExe (autologinOnceScript ''--noclear --keep-baud "$TTY" 115200,38400,9600 "$TERM"'')
else
gettyCmd "--noclear --keep-baud %I 115200,38400,9600 $TERM"
)
(pkgs.writers.writeDash "getty" autologinScript)
];
environment.TTY = "%I";
restartIfChanged = false;

View File

@@ -35,7 +35,7 @@ let
enableAutologin:
"${gettyCfg.loginProgram} ${baseLoginOptions}${lib.optionalString enableAutologin " -f -- ${gettyCfg.autologinUser}"}";
loginScript = pkgs.writers.writeBash "kmscon-login" (
loginScript = pkgs.writers.writeDash "kmscon-login" (
lib.optionalString (gettyCfg.autologinUser != null && gettyCfg.autologinOnce) ''
kms_tty=
active_tty_file=/sys/class/tty/tty0/active

View File

@@ -641,8 +641,6 @@ in
s3_http_continue_timeout = null;
s3_install_cors_rule = null;
s3_asset_cdn_url = null;
s3_role_arn = null;
s3_role_session_name = null;
max_user_api_reqs_per_minute = 20;
max_user_api_reqs_per_day = 2880;
@@ -669,6 +667,7 @@ in
compress_anon_cache = false;
anon_cache_store_threshold = 2;
allowed_theme_repos = null;
enable_email_sync_demon = false;
max_digests_enqueued_per_30_mins_per_site = 10000;
cluster_name = null;
multisite_config_path = "config/multisite.yml";
@@ -682,10 +681,6 @@ in
allow_impersonation = true;
log_line_max_chars = 160000;
yjit_enabled = false;
# this option is set to `true` in discourse's defaults.
# however, having this option enabled appears to cause a segfault in mini_racer
# see https://github.com/rubyjs/mini_racer/issues/422
mini_racer_single_threaded = false;
};
services.redis.servers.discourse =
@@ -956,35 +951,27 @@ in
# asset pipeline enables this
brotli_static on;
gzip_static on;
add_header Access-Control-Allow-Origin *;
'';
"~ ^/plugins/".extraConfig = cache_1y;
"~ /images/emoji/".extraConfig = cache_1y;
"~ ^/uploads/" = proxy {
extraConfig = ''
extraConfig = cache_1y + ''
proxy_set_header X-Sendfile-Type X-Accel-Redirect;
proxy_set_header X-Accel-Mapping ${cfg.package}/share/discourse/public/=/downloads/;
# custom CSS
location ~ /stylesheet-cache/ {
${cache_1y}
try_files $uri =404;
}
# this allows us to bypass rails
location ~* \.(gif|png|jpg|jpeg|bmp|tif|tiff|ico|webp)$ {
${cache_1y}
try_files $uri =404;
}
# force attachment so SVGs can't render as a document on direct navigation
# SVG needs an extra header attached
location ~* \.(svg)$ {
${cache_1y}
add_header Access-Control-Allow-Origin *;
add_header Content-Disposition attachment;
try_files $uri =404;
}
# thumbnails & optimized images
location ~ /_?optimized/ {
${cache_1y}
try_files $uri =404;
}
'';

View File

@@ -132,7 +132,6 @@ in
SystemCallFilter = [
"@system-service"
"~@privileged"
"@chown"
];
# User and group
DynamicUser = true;

View File

@@ -239,14 +239,11 @@ in
serviceConfig = commonConfig // {
Restart = "always";
ExecStartPre =
lib.optionals (cfg.settings.DEBUG != "True") [
"${pkg}/opt/healthchecks/manage.py compress"
]
++ [
"${pkg}/opt/healthchecks/manage.py collectstatic --no-input"
"${pkg}/opt/healthchecks/manage.py remove_stale_contenttypes --no-input"
];
ExecStartPre = [
"${pkg}/opt/healthchecks/manage.py collectstatic --no-input"
"${pkg}/opt/healthchecks/manage.py remove_stale_contenttypes --no-input"
]
++ lib.optionals (cfg.settings.DEBUG != "True") [ "${pkg}/opt/healthchecks/manage.py compress" ];
ExecStart = ''
${pkgs.python3Packages.gunicorn}/bin/gunicorn hc.wsgi \
--bind ${cfg.listenAddress}:${toString cfg.port} \

View File

@@ -1,224 +0,0 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.hister;
yamlFormat = pkgs.formats.yaml { };
dataDir = if cfg.dataDir != null then cfg.dataDir else "/var/lib/hister";
generatedConfig = yamlFormat.generate "hister-config.yml" cfg.settings;
hasConfig = cfg.configPath != null || cfg.settings != { };
runtimeConfigSource = if cfg.settings != { } then generatedConfig else cfg.configPath;
runtimeConfig = "/run/hister/config.yml";
histerEnv =
lib.optionalAttrs (cfg.port != null) {
HISTER_PORT = toString cfg.port;
}
// lib.optionalAttrs hasConfig {
HISTER_CONFIG = runtimeConfig;
}
// {
HISTER_DATA_DIR = dataDir;
};
privilegedPort = cfg.port != null && cfg.port < 1024;
in
{
meta.maintainers = with lib.maintainers; [ _4evy ];
options.services.hister = {
enable = lib.mkEnableOption "Hister, a web history service with content-based search";
package = lib.mkPackageOption pkgs "hister" { };
user = lib.mkOption {
type = lib.types.str;
default = "hister";
description = "User account under which Hister runs.";
};
group = lib.mkOption {
type = lib.types.str;
default = "hister";
description = "Group under which Hister runs.";
};
dataDir = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
example = "/var/lib/hister";
description = ''
Directory where Hister stores its data. When `null` (the default), the
service is isolated under `/var/lib/hister` via systemd's
`StateDirectory=`. When set to an explicit path, that path is created
with `systemd-tmpfiles` and granted via `ReadWritePaths=` instead.
'';
};
port = lib.mkOption {
type = lib.types.nullOr lib.types.port;
default = null;
example = 4433;
description = ''
Port on which Hister listens. When set, this overrides the port in
`server.address` from the configuration file via the `HISTER_PORT`
environment variable.
'';
};
openFirewall = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Whether to open {option}`services.hister.port` in the firewall. Has no
effect if `port` is `null`.
'';
};
configPath = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
example = "/etc/hister/config.yml";
description = ''
Path to an existing Hister configuration file mounted read-only into
the service runtime directory and passed via `HISTER_CONFIG`. Mutually
exclusive with {option}`services.hister.settings`.
'';
};
environmentFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
example = "/run/secrets/hister.env";
description = ''
Path to an environment file (read at service start) used to inject
secrets such as `HISTER__APP__ACCESS_TOKEN` without placing them in the
world-readable Nix store.
'';
};
settings = lib.mkOption {
type = yamlFormat.type;
default = { };
description = ''
Hister configuration rendered to YAML and passed via `HISTER_CONFIG`.
Accepts any structure the server accepts: see the `app`, `server`,
`indexer`, `crawler`, `hotkeys`, `extractors`, `semantic_search`, and
`sensitive_content_patterns` blocks documented upstream.
'';
example = lib.literalExpression ''
{
app = {
search_url = "https://google.com/search?q={query}";
log_level = "info";
};
server = {
address = "127.0.0.1:4433";
database = "db.sqlite3";
};
hotkeys.web = {
"/" = "focus_search_input";
"enter" = "open_result";
};
}
'';
};
};
config = lib.mkIf cfg.enable {
assertions = [
{
assertion = !(cfg.configPath != null && cfg.settings != { });
message = "Only one of services.hister.configPath and services.hister.settings can be set";
}
];
environment.systemPackages = [ cfg.package ];
users.users = lib.mkIf (cfg.user == "hister") {
hister = {
description = "Hister web history service";
group = cfg.group;
isSystemUser = true;
};
};
users.groups = lib.mkIf (cfg.group == "hister") {
hister = { };
};
systemd.tmpfiles.settings."10-hister"."${dataDir}".d = lib.mkIf (cfg.dataDir != null) {
user = cfg.user;
group = cfg.group;
mode = "0750";
};
systemd.services.hister = {
description = "Hister web history service";
after = [
"network.target"
"systemd-tmpfiles-setup.service"
"systemd-tmpfiles-resetup.service"
];
wantedBy = [ "multi-user.target" ];
environment = histerEnv;
serviceConfig = {
ExecStart = "${lib.getExe cfg.package} listen";
Restart = "on-failure";
User = cfg.user;
Group = cfg.group;
RuntimeDirectory = lib.mkIf hasConfig "hister";
RuntimeDirectoryMode = lib.mkIf hasConfig "0750";
BindReadOnlyPaths = lib.mkIf hasConfig [ "${runtimeConfigSource}:${runtimeConfig}" ];
StateDirectory = lib.mkIf (cfg.dataDir == null) "hister";
StateDirectoryMode = lib.mkIf (cfg.dataDir == null) "0750";
ReadWritePaths = lib.mkIf (cfg.dataDir != null) [ cfg.dataDir ];
EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile;
AmbientCapabilities = lib.mkIf privilegedPort [ "CAP_NET_BIND_SERVICE" ];
CapabilityBoundingSet = if privilegedPort then [ "CAP_NET_BIND_SERVICE" ] else [ "" ];
NoNewPrivileges = true;
ProtectSystem = "strict";
ProtectHome = true;
PrivateTmp = true;
PrivateDevices = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectKernelLogs = true;
ProtectControlGroups = true;
ProtectClock = true;
ProtectHostname = true;
ProtectProc = "invisible";
ProcSubset = "pid";
LockPersonality = true;
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
RemoveIPC = true;
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
SystemCallArchitectures = "native";
SystemCallFilter = [
"@system-service"
"~@privileged"
];
MemoryDenyWriteExecute = true;
UMask = "0077";
};
};
networking.firewall.allowedTCPPorts = lib.mkIf (cfg.openFirewall && cfg.port != null) [ cfg.port ];
};
}

View File

@@ -1,14 +1,12 @@
{
lib,
config,
options,
pkgs,
...
}:
let
cfg = config.services.homebox;
inherit (lib)
literalExpression
mkEnableOption
mkPackageOption
mkDefault
@@ -19,11 +17,6 @@ let
defaultUser = "homebox";
defaultGroup = "homebox";
pepperDefault =
(cfg.secrets ? HBOX_AUTH_API_KEY_PEPPER)
&& (cfg.secrets.HBOX_AUTH_API_KEY_PEPPER == "/var/lib/homebox/api-pepper-secret");
opts = options.services.homebox;
in
{
options.services.homebox = {
@@ -68,37 +61,6 @@ in
'';
};
};
secrets = mkOption {
type = types.submodule {
options = {
HBOX_AUTH_API_KEY_PEPPER = mkOption {
type = types.externalPath;
default = "/var/lib/homebox/api-pepper-secret";
description = ''
Path to the API key pepper secret file (required for homebox to start).
'';
example = "/run/secrets/homebox-api-pepper";
};
};
freeformType = types.attrsOf types.externalPath;
};
default = { };
description = ''
This follows the same structure as {option}`${opts.settings}`
but the value of each key is a path.
The specified secret path is then read by systemd via [`LoadCredential=`]
and templated into {option}`${opts.settings}` for you.
[`LoadCredential=`]: https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#Credentials
'';
example = literalExpression ''
{
HBOX_AUTH_API_KEY_PEPPER = "/run/secrets/homebox-api-pepper";
}
'';
};
};
config = mkIf cfg.enable {
@@ -130,7 +92,7 @@ in
HBOX_DATABASE_DRIVER = "sqlite3";
HBOX_DATABASE_SQLITE_PATH = "/var/lib/homebox/data/homebox.db?_pragma=busy_timeout=999&_pragma=journal_mode=WAL&_fk=1";
HBOX_OPTIONS_ALLOW_REGISTRATION = "false";
HBOX_OPTIONS_GITHUB_RELEASE_CHECK = "false";
HBOX_OPTIONS_CHECK_GITHUB_RELEASE = "false";
HBOX_MODE = "production";
# Fix this startup issue:
# failed to create modcache index dir: mkdir /var/empty/.cache: read-only file system
@@ -159,92 +121,58 @@ in
}
];
};
systemd.services.homebox-setup = mkIf pepperDefault {
script = ''
if [ ! -r "$STATE_DIRECTORY"/api-pepper-secret ]; then
umask 0277
openssl rand -base64 48 > "$STATE_DIRECTORY"/api-pepper-secret
fi
systemd.services.homebox = {
requires = lib.optional cfg.database.createLocally "postgresql.target";
after = lib.optional cfg.database.createLocally "postgresql.target";
environment = lib.filterAttrs (_: v: v != null) cfg.settings;
preStart = ''
"${pkgs.coreutils}/bin/rm" -rf /var/lib/homebox/tmp
"${pkgs.coreutils}/bin/mkdir" -p /var/lib/homebox/tmp
'';
path = [
pkgs.openssl
];
serviceConfig = {
Type = "oneshot";
User = cfg.user;
Group = cfg.group;
ExecStart = lib.getExe cfg.package;
LimitNOFILE = "1048576";
PrivateTmp = true;
PrivateDevices = true;
Restart = "always";
StateDirectory = "homebox";
# Hardening
CapabilityBoundingSet = "";
LockPersonality = true;
MemoryDenyWriteExecute = true;
PrivateUsers = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProcSubset = "pid";
ProtectSystem = "strict";
RestrictAddressFamilies = [
"AF_UNIX"
"AF_INET"
"AF_INET6"
"AF_NETLINK"
];
RestrictNamespaces = true;
RestrictRealtime = true;
SystemCallArchitectures = "native";
SystemCallFilter = [
"@system-service"
"@pkey"
];
RestrictSUIDSGID = true;
PrivateMounts = true;
UMask = "0077";
};
wantedBy = [ "multi-user.target" ];
};
systemd.services.homebox =
let
deps =
lib.optionals pepperDefault [
"homebox-setup.service"
]
++ lib.optionals cfg.database.createLocally [
"postgresql.target"
];
in
{
requires = deps;
after = deps;
environment = lib.filterAttrs (_: v: v != null) cfg.settings;
preStart = ''
"${pkgs.coreutils}/bin/rm" -rf /var/lib/homebox/tmp
"${pkgs.coreutils}/bin/mkdir" -p /var/lib/homebox/tmp
'';
script = ''
${lib.strings.concatLines (
lib.mapAttrsToList (name: _: "export ${name}=$(<\"$CREDENTIALS_DIRECTORY\"/${name})") cfg.secrets
)}
exec ${lib.getExe cfg.package}
'';
serviceConfig = {
User = cfg.user;
Group = cfg.group;
LoadCredential = (lib.mapAttrsToList (name: path: "${name}:${path}") cfg.secrets);
LimitNOFILE = "1048576";
PrivateTmp = true;
PrivateDevices = true;
Restart = "always";
StateDirectory = "homebox";
# Hardening
CapabilityBoundingSet = "";
LockPersonality = true;
MemoryDenyWriteExecute = true;
PrivateUsers = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProcSubset = "pid";
ProtectSystem = "strict";
RestrictAddressFamilies = [
"AF_UNIX"
"AF_INET"
"AF_INET6"
"AF_NETLINK"
];
RestrictNamespaces = true;
RestrictRealtime = true;
SystemCallArchitectures = "native";
SystemCallFilter = [
"@system-service"
"@pkey"
];
RestrictSUIDSGID = true;
PrivateMounts = true;
UMask = "0077";
};
wantedBy = [ "multi-user.target" ];
};
};
meta.maintainers = with lib.maintainers; [
patrickdag

View File

@@ -208,13 +208,7 @@ in
abi <abi/4.0>,
include <tunables/global>
# Flag `attach_disconnected` is necessary
# because the PostgreSQL socket path appears
# as a "disconnected" path: `run/postgresql/.s.PGSQL.XXXX`,
# without the trailing slash, which AppArmor can't resolve.
# The flag prepends a `/`, which isn't recommended,
# but there aren't any alternative currently.
profile ${cfg.package}/bin/miniflux flags=(attach_disconnected) {
profile ${cfg.package}/bin/miniflux {
include <abstractions/base>
include <abstractions/nameservice>
include <abstractions/ssl_certs>
@@ -222,8 +216,6 @@ in
include "${pkgs.apparmorRulesFromClosure { name = "miniflux"; } cfg.package}"
${cfg.package}/bin/miniflux r,
/run/miniflux/** rw,
/run/postgresql/.s.PGSQL.* rw,
/run/credentials/** r,
include if exists <local/bin.miniflux>
}
'';

View File

@@ -66,9 +66,7 @@ in
WorkingDirectory = "/var/lib/readeck";
EnvironmentFile = lib.optional (cfg.environmentFile != null) cfg.environmentFile;
DynamicUser = true;
# readeck opens config.toml as writable in case it needs to add a secret key...
ExecStartPre = "${lib.getExe' pkgs.coreutils "cp"} --no-preserve=all ${configFile} config.toml";
ExecStart = "${lib.getExe cfg.package} serve -config config.toml";
ExecStart = "${lib.getExe cfg.package} serve -config ${configFile}";
ProtectSystem = "full";
SystemCallArchitectures = "native";
MemoryDenyWriteExecute = true;

View File

@@ -90,7 +90,6 @@ let
"unixd"
"slotmem_shm"
"socache_shmcb"
"systemd"
"mpm_${cfg.mpm}"
]
++ (if cfg.mpm == "prefork" then [ "cgi" ] else [ "cgid" ])
@@ -993,12 +992,12 @@ in
'';
serviceConfig = {
ExecStart = "${pkg}/bin/httpd -D FOREGROUND -f /etc/httpd/httpd.conf";
ExecStart = "@${pkg}/bin/httpd httpd -f /etc/httpd/httpd.conf";
ExecStop = "${pkg}/bin/httpd -f /etc/httpd/httpd.conf -k graceful-stop";
ExecReload = "${pkg}/bin/httpd -f /etc/httpd/httpd.conf -k graceful";
KillMode = "mixed";
User = cfg.user;
Group = cfg.group;
Type = "notify";
Type = "forking";
PIDFile = "${runtimeDir}/httpd.pid";
Restart = "always";
RestartSec = "5s";

View File

@@ -1614,7 +1614,11 @@ in
];
RestrictNamespaces = true;
LockPersonality = true;
MemoryDenyWriteExecute = false; # for pcre2 & several plugins
MemoryDenyWriteExecute =
!(
(builtins.any (mod: (mod.allowMemoryWriteExecute or false)) cfg.package.modules)
|| (lib.getName cfg.package == "openresty")
);
RestrictRealtime = true;
RestrictSUIDSGID = true;
RemoveIPC = true;

View File

@@ -139,7 +139,7 @@ in
"AF_INET6"
];
LockPersonality = true;
MemoryDenyWriteExecute = false; # pcre2 jit
MemoryDenyWriteExecute = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
PrivateMounts = true;

Some files were not shown because too many files have changed in this diff Show More