Merge d9c4084332 into haskell-updates

This commit is contained in:
nixpkgs-ci[bot] 2026-06-11 00:55:35 +00:00 committed by GitHub
commit cac8c10692
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
1008 changed files with 18836 additions and 9960 deletions

View file

@ -2,7 +2,7 @@ name: Teams
on:
schedule:
# Every Tuesday at 19:42 (randomly chosen)
# Every Monday at 19:42 (randomly chosen)
- cron: '42 19 * * 1'
workflow_dispatch:

View file

@ -17,6 +17,7 @@ jopejoe1 <nixpkgs@missing.ninja> <34899572+jopejoe1@users.noreply.github.com>
jopejoe1 <nixpkgs@missing.ninja> <jopejoe1@missing.ninja>
jopejoe1 <nixpkgs@missing.ninja> <jopejoe1>
Jörg Thalheim <joerg@thalheim.io> <Mic92@users.noreply.github.com>
Katalin Rebhan <me@dblsaiko.net>
Lin Jian <me@linj.tech> <linj.dev@outlook.com>
Lin Jian <me@linj.tech> <75130626+jian-lin@users.noreply.github.com>
Martin Weinelt <hexa@darmstadt.ccc.de> <mweinelt@users.noreply.github.com>

View file

@ -411,9 +411,9 @@ pkgs/development/python-modules/buildcatrust/ @ajs124 @lukegb @mweinelt
/pkgs/development/compilers/go @kalbasit @katexochen @Mic92
# GNOME
/pkgs/desktops/gnome @jtojnar
/pkgs/desktops/gnome/extensions @jtojnar
/pkgs/build-support/make-hardcode-gsettings-patch @jtojnar
/pkgs/desktops/gnome @NixOS/gnome
/pkgs/desktops/gnome/extensions @NixOS/gnome
/pkgs/build-support/make-hardcode-gsettings-patch @NixOS/gnome
# Cinnamon
/pkgs/by-name/ci/cinnamon-* @mkg20001

View file

@ -9,9 +9,9 @@
},
"branch": "nixpkgs-unstable",
"submodules": false,
"revision": "02f3fa0374fa13707d42d55d58ecc76b091f223c",
"url": "https://github.com/NixOS/nixpkgs/archive/02f3fa0374fa13707d42d55d58ecc76b091f223c.tar.gz",
"hash": "0z8d33c5g0gk9a74ppqq77npisf9xx9c8ai9isxa2hyjx4lv1pki"
"revision": "cbb5cf358f50aa6acc9efd6113b7bcfbc352cd73",
"url": "https://github.com/NixOS/nixpkgs/archive/cbb5cf358f50aa6acc9efd6113b7bcfbc352cd73.tar.gz",
"hash": "sha256-IX7G1dlKrOqPOImfbo7ADDfV5yU1+j+MRChI3TL4tAA="
},
"treefmt-nix": {
"type": "Git",
@ -22,10 +22,10 @@
},
"branch": "main",
"submodules": false,
"revision": "790751ff7fd3801feeaf96d7dc416a8d581265ba",
"url": "https://github.com/numtide/treefmt-nix/archive/790751ff7fd3801feeaf96d7dc416a8d581265ba.tar.gz",
"hash": "1zah3dmbpn3ap5acg22kq1j19dg32gj73l43yamjcxhc38sv9kd5"
"revision": "db947814a175b7ca6ded66e21383d938df01c227",
"url": "https://github.com/numtide/treefmt-nix/archive/db947814a175b7ca6ded66e21383d938df01c227.tar.gz",
"hash": "sha256-eynAfOmbmxJnkp7YewvCEbShNnnYJ9gLLqkzsYtBPeM="
}
},
"version": 5
"version": 8
}

View file

@ -1,8 +1,9 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p npins
#!nix-shell -i bash -p npins -I nixpkgs=../
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")"
npins --lock-file pinned.json upgrade
npins --lock-file pinned.json update

View file

@ -98,6 +98,14 @@ It has two modes:
: The path to the files to check.
`relocatable` (boolean, optional) {#tester-lycheeLinkCheck-param-relocatable}
: Whether the site is expected to be relocatable, i.e. servable from any URL path prefix.
When `true` (the default), root-relative links (starting with `/`) are treated as errors, because they break when the site is served from a subpath or opened via `file://` URLs.
When `false`, root-relative links are resolved against the `site` directory.
`remap` (attribute set, optional) {#tester-lycheeLinkCheck-param-remap}
: An attribute set where the attribute names are regular expressions.

View file

@ -19,6 +19,13 @@
- `uhttpmock` providing 0.0 ABI was removed. `uhttpmock_1_0` providing 1.0 ABI was renamed to `uhttpmock` and `uhttpmock_1_0` was kept as an alias.
- 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.
- `linux-kernel.installTarget` has been removed, as it should not be necessary to customize.
- `linux-kernel.DTB` is available as the `buildDTBs` parameter and passthru attribute on the kernel builders.
- `linux-kernel.{autoModules,preferBuiltin,extraConfig}` were already available as kernel builder parameters.
- The ARMv5 Linux kernel build now uses a standard configuration and generates a standard compressed image instead of the deprecated legacy UBoot image format.
`lib.systems.{examples,platforms}.{sheevaplug,pogoplug4}` have been unified into `lib.systems.examples.armv5tel-multiplatform`.
Note that there is no official support for ARMv5 and it is not possible to build even a simple NixOS configuration out of the box.

View file

@ -1018,38 +1018,16 @@ let
mergedType = t.typeMerge t'.functor;
typesMergeable = mergedType != null;
# TODO: Remove this when all downstream reliances of internals: 'functor.wrapped' are sufficiently migrated.
# A function that adds the deprecated wrapped message to a type.
addDeprecatedWrapped =
t:
t
// {
functor = t.functor // {
wrapped = t.functor.wrappedDeprecationMessage {
inherit loc;
};
};
};
typeSet =
if opt.options ? type then
if res ? type then
if typesMergeable then
{
type =
if mergedType ? functor.wrappedDeprecationMessage then
addDeprecatedWrapped mergedType
else
mergedType;
}
else
# Keep in sync with the same error below!
throw
"The option `${showOption loc}' in `${opt._file}' is already declared in ${showFiles res.declarations}."
else if opt.options.type ? functor.wrappedDeprecationMessage then
{ type = addDeprecatedWrapped opt.options.type; }
if opt.options ? type && res ? type then
if typesMergeable then
{
type = mergedType;
}
else
{ }
# Keep in sync with the same error below!
throw
"The option `${showOption loc}' in `${opt._file}' is already declared in ${showFiles res.declarations}."
else
{ };

View file

@ -299,12 +299,10 @@ let
inherit
(
{
linux-kernel = args.linux-kernel or { };
gcc = args.gcc or { };
}
// platforms.select final
)
linux-kernel
gcc
;
@ -692,6 +690,11 @@ let
};
};
in
# TODO: Remove in 27.05.
assert
args ? linux-kernel
-> throw "lib.systems.elaborate: linux-kernel has been removed; see the 26.11 release notes";
assert final.useAndroidPrebuilt -> final.isAndroid;
assert foldl' (pass: { assertion, message }: if assertion final then pass else throw message) true (
final.parsed.abi.assertions or [ ]

View file

@ -3,74 +3,21 @@
# targetPlatform, etc) containing at least the minimal set of attrs
# required (see types.parsedPlatform in lib/systems/parse.nix). This
# file takes an already-valid platform and further elaborates it with
# optional fields; currently these are: linux-kernel, gcc, and rustc.
# optional fields; currently these are: gcc, and rustc.
{ lib }:
rec {
pc = {
linux-kernel = {
name = "pc";
baseConfig = "defconfig";
# Build whatever possible as a module, if not stated in the extra config.
autoModules = true;
target = "bzImage";
};
};
##
## POWER
##
powernv = {
linux-kernel = {
name = "PowerNV";
baseConfig = "powernv_defconfig";
target = "vmlinux";
autoModules = true;
};
};
ppc64 = {
linux-kernel = {
name = "powerpc64";
baseConfig = "ppc64_defconfig";
target = "vmlinux";
autoModules = true;
};
};
##
## ARM
##
armv5tel-multiplatform = {
linux-kernel = {
name = "armv5tel-multiplatform";
baseConfig = "multi_v5_defconfig";
DTB = true;
autoModules = true;
preferBuiltin = true;
target = "zImage";
};
gcc = {
arch = "armv5te";
};
};
raspberrypi = {
linux-kernel = {
name = "raspberrypi";
baseConfig = "bcm2835_defconfig";
DTB = true;
autoModules = true;
preferBuiltin = true;
target = "zImage";
};
gcc = {
# https://en.wikipedia.org/wiki/Raspberry_Pi#Specifications
arch = "armv6kz";
@ -105,7 +52,6 @@ rec {
# https://developer.android.com/ndk/guides/abis#v7a
armv7a-android = {
linux-kernel.name = "armeabi-v7a";
gcc = {
arch = "armv7-a";
float-abi = "softfp";
@ -114,14 +60,6 @@ rec {
};
armv7l-hf-multiplatform = {
linux-kernel = {
name = "armv7l-hf-multiplatform";
baseConfig = "defconfig";
DTB = true;
autoModules = true;
preferBuiltin = true;
target = "zImage";
};
gcc = {
# Some table about fpu flags:
# http://community.arm.com/servlet/JiveServlet/showImage/38-1981-3827/blogentry-103749-004812900+1365712953_thumb.png
@ -146,19 +84,6 @@ rec {
};
aarch64-multiplatform = {
linux-kernel = {
name = "aarch64-multiplatform";
baseConfig = "defconfig";
DTB = true;
autoModules = true;
preferBuiltin = true;
extraConfig = ''
# The default (=y) forces us to have the XHCI firmware available in initrd,
# which our initrd builder can't currently do easily.
USB_XHCI_TEGRA m
'';
target = "Image";
};
gcc = {
arch = "armv8-a";
};
@ -176,9 +101,6 @@ rec {
##
ben_nanonote = {
linux-kernel = {
name = "ben_nanonote";
};
gcc = {
arch = "mips32";
float = "soft";
@ -235,17 +157,6 @@ rec {
## Other
##
riscv-multiplatform = {
linux-kernel = {
name = "riscv-multiplatform";
target = "Image";
autoModules = true;
preferBuiltin = true;
baseConfig = "defconfig";
DTB = true;
};
};
loongarch64-multiplatform = {
gcc = {
# https://github.com/loongson/la-softdev-convention/blob/master/la-softdev-convention.adoc#10-operating-system-package-build-requirements
@ -257,14 +168,6 @@ rec {
# https://github.com/llvm/llvm-project/pull/132173
cmodel = "medium";
};
linux-kernel = {
name = "loongarch-multiplatform";
target = "vmlinuz.efi";
autoModules = true;
preferBuiltin = true;
baseConfig = "defconfig";
DTB = true;
};
};
# This function takes a minimally-valid "platform" and returns an
@ -272,17 +175,13 @@ rec {
# included in the platform in order to further elaborate it.
select =
platform:
# x86
if platform.isx86 then
pc
# ARM
else if platform.isAarch32 then
if platform.isAarch32 then
let
version = platform.parsed.cpu.version or null;
in
if version == null then
pc
{ }
else if lib.versionOlder version "6" then
armv5tel-multiplatform
else if lib.versionOlder version "7" then
@ -296,24 +195,9 @@ rec {
else if platform.isLoongArch64 then
loongarch64-multiplatform
else if platform.isRiscV then
riscv-multiplatform
else if platform.parsed.cpu == lib.systems.parse.cpuTypes.mipsel then
(import ./examples.nix { inherit lib; }).mipsel-linux-gnu
else if platform.isPower64 then
if platform.isLittleEndian then powernv else ppc64
else if platform.isSh4 then
{
linux-kernel = {
target = "vmlinux";
# SH arch doesn't have a 'make install' target.
installTarget = "vmlinux";
};
}
else
{ };
}

View file

@ -515,56 +515,6 @@ checkConfigError 'infinite recursion encountered' config.nonLazyResult ./lazy-at
checkConfigOutput '^"mergedName.<id>.nested"$' config.result ./name-merge-attrsWith-1.nix
checkConfigError 'The option .mergedName. in .*\.nix. is already declared in .*\.nix' config.mergedName ./name-merge-attrsWith-2.nix
# Test type.functor.wrapped deprecation warning
# should emit the warning on:
# - merged types
# - non-merged types
# - nestedTypes elemType
# attrsWith
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.attrsWith.type.functor.wrapped ./deprecated-wrapped.nix
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedAttrsWith.type.functor.wrapped ./deprecated-wrapped.nix
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.attrsWith.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedAttrsWith.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
# listOf
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.listOf.type.functor.wrapped ./deprecated-wrapped.nix
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedListOf.type.functor.wrapped ./deprecated-wrapped.nix
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.listOf.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedListOf.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
# unique / uniq
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.unique.type.functor.wrapped ./deprecated-wrapped.nix
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedUnique.type.functor.wrapped ./deprecated-wrapped.nix
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.unique.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedUnique.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
# nullOr
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.nullOr.type.functor.wrapped ./deprecated-wrapped.nix
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedNullOr.type.functor.wrapped ./deprecated-wrapped.nix
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.nullOr.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedNullOr.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
# functionTo
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.functionTo.type.functor.wrapped ./deprecated-wrapped.nix
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedFunctionTo.type.functor.wrapped ./deprecated-wrapped.nix
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.functionTo.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedFunctionTo.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
# coercedTo
# Note: test 'nestedTypes.finalType' and 'nestedTypes.coercedType'
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.coercedTo.type.functor.wrapped ./deprecated-wrapped.nix
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.coercedTo.type.nestedTypes.finalType.functor.wrapped ./deprecated-wrapped.nix
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.coercedTo.type.nestedTypes.coercedType.functor.wrapped ./deprecated-wrapped.nix
# either
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.either.type.functor.wrapped ./deprecated-wrapped.nix
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedEither.type.functor.wrapped ./deprecated-wrapped.nix
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.either.type.nestedTypes.left.functor.wrapped ./deprecated-wrapped.nix
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.either.type.nestedTypes.right.functor.wrapped ./deprecated-wrapped.nix
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedEither.type.nestedTypes.left.functor.wrapped ./deprecated-wrapped.nix
ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedEither.type.nestedTypes.right.functor.wrapped ./deprecated-wrapped.nix
# Even with multiple assignments, a type error should be thrown if any of them aren't valid
checkConfigError 'A definition for option .* is not of type .*' \
config.value ./declare-int-unsigned-value.nix ./define-value-list.nix ./define-value-int-positive.nix
@ -733,9 +683,6 @@ checkConfigError 'The option .theOption.nested. in .other.nix. is already declar
# Test that types.optionType leaves types untouched as long as they don't need to be merged
checkConfigOutput 'ok' config.freeformItems.foo.bar ./adhoc-freeformType-survives-type-merge.nix
# Test that specifying both functor.wrapped and functor.payload isn't allowed
checkConfigError 'Type foo defines both `functor.payload` and `functor.wrapped` at the same time, which is not supported.' config.result ./default-type-merge-both.nix
# Test that not including functor.wrapped is allowed
checkConfigOutput 'ok' config.result ./default-type-merge-payload.nix

View file

@ -1,28 +0,0 @@
{ lib, options, ... }:
let
foo = lib.mkOptionType {
name = "foo";
functor = lib.types.defaultFunctor "foo" // {
wrapped = lib.types.int;
payload = 10;
};
};
in
{
imports = [
{
options.foo = lib.mkOption {
type = foo;
};
}
{
options.foo = lib.mkOption {
type = foo;
};
}
];
options.result = lib.mkOption {
default = builtins.seq options.foo null;
};
}

View file

@ -99,7 +99,6 @@ let
{ elemType, ... }@payload:
{
inherit name payload;
wrappedDeprecationMessage = makeWrappedDeprecationMessage payload;
type = types.${name};
binOp =
a: b:
@ -108,14 +107,6 @@ let
in
if merged == null then null else { elemType = merged; };
};
makeWrappedDeprecationMessage =
payload:
{ loc }:
lib.warn ''
The deprecated `${lib.optionalString (loc != null) "type."}functor.wrapped` attribute ${
lib.optionalString (loc != null) "of the option `${showOption loc}` "
}is accessed, use `${lib.optionalString (loc != null) "type."}nestedTypes.elemType` instead.
'' payload.elemType;
checkDefsForError =
check: loc: defs:
@ -168,21 +159,11 @@ rec {
defaultTypeMerge =
f: f':
let
mergedWrapped = f.wrapped.typeMerge f'.wrapped.functor;
mergedPayload = f.binOp f.payload f'.payload;
hasPayload =
assert (f'.payload != null) == (f.payload != null);
f.payload != null;
hasWrapped =
let
hasWrappedNonNull = set: set ? "wrapped" && set.wrapped != null;
in
assert (hasWrappedNonNull f') == (hasWrappedNonNull f);
hasWrappedNonNull f;
typeFromPayload = if mergedPayload == null then null else f.type mergedPayload;
typeFromWrapped = if mergedWrapped == null then null else f.type mergedWrapped;
in
# Abort early: cannot merge different types
if f.name != f'.name then
@ -190,22 +171,7 @@ rec {
else
if hasPayload then
# Just return the payload if returning wrapped is deprecated
if f ? wrappedDeprecationMessage then
typeFromPayload
else if hasWrapped then
# Has both wrapped and payload
throw ''
Type ${f.name} defines both `functor.payload` and `functor.wrapped` at the same time, which is not supported.
Use either `functor.payload` or `functor.wrapped` but not both.
If your code worked before remove either `functor.wrapped` or `functor.payload` from the type definition.
''
else
typeFromPayload
else if hasWrapped then
typeFromWrapped
if mergedPayload == null then null else f.type mergedPayload
else
f.type;
@ -213,7 +179,6 @@ rec {
defaultFunctor = name: {
inherit name;
type = lib.types.${name} or null;
wrapped = null;
payload = null;
binOp = a: b: null;
};
@ -309,17 +274,8 @@ rec {
deprecationMessage
nestedTypes
descriptionClass
functor
;
functor =
if functor ? wrappedDeprecationMessage then
functor
// {
wrapped = functor.wrappedDeprecationMessage {
loc = null;
};
}
else
functor;
description = if description == null then name else description;
};
@ -1834,9 +1790,7 @@ rec {
getSubModules = finalType.getSubModules;
substSubModules = m: coercedTo coercedType coerceFunc (finalType.substSubModules m);
typeMerge = t: null;
functor = (defaultFunctor name) // {
wrappedDeprecationMessage = makeWrappedDeprecationMessage { elemType = finalType; };
};
functor = defaultFunctor name;
nestedTypes.coercedType = coercedType;
nestedTypes.finalType = finalType;
};

View file

@ -2,10 +2,11 @@
"acme": {
"description": "Maintain ACME-related packages and modules.",
"id": 3806126,
"maintainers": {},
"maintainers": {
"emilazy": 18535642
},
"members": {
"arianvp": 628387,
"emilazy": 18535642,
"m1cr0man": 3044438
},
"name": "ACME"
@ -71,11 +72,10 @@
"description": "coordinates efforts towards bootstrappable builds (see https://bootstrappable.org/)",
"id": 9141350,
"maintainers": {
"philiptaron": 43863,
"zeuner": 2545850
},
"members": {
"philiptaron": 43863
},
"members": {},
"name": "bootstrapping"
},
"categorization": {
@ -176,6 +176,7 @@
"description": "Improve Darwin-support across Nixpkgs and help maintainers without access to Darwin hardware. Apply to join through https://github.com/NixOS/nixpkgs/issues/323144 to keep the process transparent.",
"id": 2385202,
"maintainers": {
"emilazy": 18535642,
"toonn": 1486805
},
"members": {
@ -205,7 +206,6 @@
"donn": 12652988,
"dwt": 57199,
"eclairevoyant": 848000,
"emilazy": 18535642,
"ethancedwards8": 60861925,
"fiddlerwoaroof": 808745,
"fulsomenko": 14945057,
@ -410,9 +410,7 @@
"jtojnar": 705123
},
"members": {
"bobby285271": 20080233,
"dasj19": 7589338,
"hedning": 71978
"bobby285271": 20080233
},
"name": "GNOME"
},
@ -556,11 +554,12 @@
"id": 9955829,
"maintainers": {
"RossComputerGuy": 19699320,
"alyssais": 2768870
"alyssais": 2768870,
"emilazy": 18535642
},
"members": {
"Ericson2314": 1055245,
"emilazy": 18535642,
"peterwaller-arm": 52030119,
"rrbutani": 7833358,
"sternenseemann": 3154475
},
@ -695,8 +694,7 @@
"description": "",
"id": 174820,
"maintainers": {
"Ericson2314": 1055245,
"tomberek": 178444
"Ericson2314": 1055245
},
"members": {
"Mic92": 96200,
@ -704,6 +702,7 @@
"edolstra": 1148549,
"lisanna-dettwyler": 72424138,
"lovesegfault": 7243783,
"tomberek": 178444,
"xokdvium": 145775305
},
"name": "Nix team"
@ -852,15 +851,15 @@
"description": "Team that is interested in reproducible builds",
"id": 7625643,
"maintainers": {
"raboof": 131856,
"zimbatm": 3248
"raboof": 131856
},
"members": {
"Artturin": 56650223,
"Atemu": 18599032,
"RaitoBezarius": 314564,
"davidak": 91113,
"mschwaig": 3856390
"mschwaig": 3856390,
"zimbatm": 3248
},
"name": "reproducible"
},
@ -964,13 +963,13 @@
"description": "Maintain the standard environment and its surrounding logic.",
"id": 11265412,
"maintainers": {
"RossComputerGuy": 19699320
"RossComputerGuy": 19699320,
"emilazy": 18535642,
"philiptaron": 43863
},
"members": {
"Artturin": 56650223,
"Ericson2314": 1055245,
"emilazy": 18535642,
"philiptaron": 43863,
"reckenrode": 7413633
},
"name": "stdenv"

View file

@ -813,6 +813,13 @@
githubId = 19290901;
name = "Andrew Brooks";
};
agentelement = {
email = "agentelement@agentelement.net";
github = "agentelement";
githubId = 38045210;
name = "AgentElement";
keys = [ { fingerprint = "D37E 581D AD71 2378 A622 5BC0 CD13 9E3B 4354 34F1"; } ];
};
agilesteel = {
email = "agilesteel@gmail.com";
github = "agilesteel";
@ -2126,6 +2133,12 @@
githubId = 56009;
name = "Arcadio Rubio García";
};
arcayr = {
email = "nixpkgs@arcayr.online";
github = "arcayr";
githubId = 11192354;
name = "Elliot Speck";
};
archer-65 = {
email = "mario.liguori.056@gmail.com";
github = "archer-65";
@ -2319,6 +2332,12 @@
githubId = 3965744;
name = "Arthur Lee";
};
arthurficial = {
email = "arti.ficial@fullstackoptimization.com";
github = "Arthur-Ficial";
githubId = 258112064;
name = "Arthur Ficial";
};
arthurteisseire = {
email = "arthurteisseire33@gmail.com";
github = "arthurteisseire";
@ -6686,6 +6705,12 @@
githubId = 90097;
name = "Derek Collison";
};
DerGrumpf = {
email = "phil.keier@hotmail.com";
github = "DerGrumpf";
githubId = 17272572;
name = "Phil Keier";
};
DerGuteMoritz = {
email = "moritz@twoticketsplease.de";
github = "DerGuteMoritz";
@ -7945,6 +7970,11 @@
githubId = 18375468;
name = "Elliot Xu";
};
elliotberman = {
name = "Elliot Berman";
github = "elliotberman";
githubId = 210410075;
};
elliottslaughter = {
name = "Elliott Slaughter";
email = "elliottslaughter@gmail.com";
@ -9639,6 +9669,11 @@
githubId = 293586;
name = "Adam Gamble";
};
gamebeaker = {
github = "gamebeaker";
githubId = 9269847;
name = "gamebeaker";
};
gamedungeon = {
github = "GameDungeon";
githubId = 60719255;
@ -10681,12 +10716,6 @@
githubId = 2427959;
name = "Hector Jusforgues";
};
hedning = {
email = "torhedinbronner@gmail.com";
github = "hedning";
githubId = 71978;
name = "Tor Hedin Brønner";
};
heel = {
email = "parizhskiy@gmail.com";
github = "HeeL";
@ -10912,6 +10941,11 @@
githubId = 58676303;
name = "hhydraa";
};
hibiday = {
name = "Katsumi Takeuchi";
github = "hibiday";
githubId = 137286929;
};
higebu = {
name = "Yuya Kusakabe";
email = "yuya.kusakabe@gmail.com";
@ -12093,6 +12127,12 @@
github = "Jaculabilis";
githubId = 10787844;
};
jadewilk = {
name = "Jade Wilk";
email = "p.wilk@student.reading.ac.uk";
github = "jadewilk";
githubId = 50517631;
};
jaduff = {
email = "jdduffpublic@proton.me";
github = "jaduff";
@ -13523,6 +13563,12 @@
githubId = 5352661;
name = "James Cleverley-Prance";
};
jpz13 = {
email = "jp@pipekit.io";
github = "JPZ13";
githubId = 6625432;
name = "J.P. Zivalich";
};
jqueiroz = {
email = "nixos@johnjq.com";
github = "jqueiroz";
@ -21609,12 +21655,6 @@
githubId = 43863;
name = "Philip Taron";
};
philipwilk = {
name = "Philip Wilk";
email = "p.wilk@student.reading.ac.uk";
github = "philipwilk";
githubId = 50517631;
};
philocalyst = {
name = "Myles Wirth";
email = "milestheperson@posteo.net";
@ -23342,6 +23382,11 @@
github = "Reputable2772";
githubId = 153411261;
};
ReStranger = {
name = "ReStranger";
github = "ReStranger";
githubId = 69393944;
};
ret2pop = {
email = "ret2pop@gmail.com";
github = "ret2pop";
@ -27034,6 +27079,12 @@
githubId = 6064962;
name = "TakWolf";
};
talal = {
email = "noreply@talal.ch";
github = "talal";
githubId = 3526562;
name = "Muhammad Talal Anwar";
};
talhaHavadar = {
email = "havadartalha@gmail.com";
github = "talhaHavadar";
@ -30150,6 +30201,12 @@
githubId = 36407913;
name = "Uli Baum";
};
xelacodes = {
name = "Alex";
github = "itzTheMeow";
githubId = 50887230;
email = "nixpkgs@xela.codes";
};
xelden = {
email = "anpiz@protonmail.com";
github = "Xelden";

View file

@ -12,6 +12,7 @@ cassowary,,,,,,alerque
cldr,,,,,,alerque
commons.nvim,,,,,5.1,mrcjkb
compat53,,,,,,vcunat
coop.nvim,,,,,,
cosmo,,,,,,
coxpcall,,,,1.17.0-1,,
cqueues,,,,,,vcunat
@ -53,8 +54,8 @@ lrexlib-pcre2,,,,,,wishstudio
lrexlib-posix,,,,,,
lsp-progress.nvim,,,,,5.1,gepbird
lsqlite3,,,,,,
ltreesitter-ts,,,,,,
ltreesitter,,,,,,
ltreesitter-ts,,,,,,
lua-cjson,,,,,,
lua-cmsgpack,,,,,,
lua-curl,,,,,,
@ -174,8 +175,8 @@ tree-sitter-http,,,,0.0.33-1,,
tree-sitter-norg,,,,,5.1,mrcjkb
tree-sitter-norg-meta,,,,,,
tree-sitter-orgmode,,,,,5.1,
utf8,,,,,,
tree-sitter-teal,,,,,,
utf8,,,,,,
vicious,,,,,,
vstruct,,,,,,
vusted,,,,,,

1 name rockspec ref server version luaversion maintainers
12 cldr alerque
13 commons.nvim 5.1 mrcjkb
14 compat53 vcunat
15 coop.nvim
16 cosmo
17 coxpcall 1.17.0-1
18 cqueues vcunat
54 lrexlib-posix
55 lsp-progress.nvim 5.1 gepbird
56 lsqlite3
ltreesitter-ts
57 ltreesitter
58 ltreesitter-ts
59 lua-cjson
60 lua-cmsgpack
61 lua-curl
175 tree-sitter-norg 5.1 mrcjkb
176 tree-sitter-norg-meta
177 tree-sitter-orgmode 5.1
utf8
178 tree-sitter-teal
179 utf8
180 vicious
181 vstruct
182 vusted

View file

@ -130,6 +130,8 @@
- [linkding](https://linkding.link/), a self-hosted bookmark manager designed to be minimal, fast, and easy to set up. Available as [services.linkding](#opt-services.linkding.enable).
- [gs1200-exporter](https://github.com/robinelfrink/gs1200-exporter), a Prometheus exporter for Zyxel GS1200 series switches. Available as [services.gs1200-exporter](#opt-services.gs1200-exporter.enable).
- [Tinyauth](https://tinyauth.app/), a simple authentication middleware for web apps, with OAuth and LDAP support. Available as [services.tinyauth](#opt-services.tinyauth.enable).
- [Strichliste](https://www.strichliste.org), a digital self-service tallysheet used in hackerspaces, clubs and offices. Available as [services.strichliste](#opt-services.strichliste.enable).

View file

@ -12,6 +12,8 @@
- [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).
- [scx_loader](https://github.com/sched-ext/scx-loader), a system daemon and DBus-based loader for sched_ext schedulers. `scxctl` is the command-line client for interacting with the loader, allowing users to switch schedulers, modes, and arguments dynamically. Available as [services.scx-loader](#opt-services.scx-loader.enable)
- [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).
## Backward Incompatibilities {#sec-release-26.11-incompatibilities}
@ -22,10 +24,16 @@
- Support for the legacy UBoot image format has been removed from the initrd generators, as it is deprecated upstream and no longer used by any platform in Nixpkgs.
- `services.llama-cpp` is now configured using structured `services.llama-cpp.settings` attribute.
- Python 2 has been removed from the top-level package set, as it is long past end-of-life. The `python2`, `python27`, `python2Full`, `python27Full`, `python2Packages`, and `python27Packages` attributes, along with the legacy `python`, `pythonFull`, and `pythonPackages` aliases, now throw an error directing you to `python3`. The `isPy2` and `isPy27` package flags have been removed accordingly. The only remaining Python 2 interpreter is vendored inside the `resholve` package for its `oil` dependency and is not exposed for general use.
- `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;`.
- `services.timesyncd.extraConfig` has been removed in favor of the structured [](#opt-services.timesyncd.settings.Time) option. Use `services.timesyncd.settings.Time` to set any `timesyncd.conf(5)` option directly. For example, replace `services.timesyncd.extraConfig = "PollIntervalMaxSec=180";` with `services.timesyncd.settings.Time.PollIntervalMaxSec = 180;`.
- `services.firezone.server.provision` has been removed due to it being unmaintanable. Remove all uses of provisioning and use the WebUI to configure firezone.
## Other Notable Changes {#sec-release-26.11-notable-changes}
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->

View file

@ -14,7 +14,7 @@ let
inherit (config) sshBackdoor;
inherit (hostPkgs.stdenv.hostPlatform) isLinux;
inherit (hostPkgs.stdenv.hostPlatform) isLinux isAarch64;
# Reifies and correctly wraps the python test driver for
# the respective qemu version and with or without ocr support
@ -256,6 +256,10 @@ in
#
# If needed, this can still be turned off.
virtualisation.qemu.enableSharedMemory = lib.mkDefault isLinux;
# Needed for screenshots to work (in e.g `nixosTests.login`)
virtualisation.qemu.options = lib.optionals (isLinux && isAarch64) [
"-device virtio-gpu-pci"
];
assertions = [
{

View file

@ -950,32 +950,8 @@ in
};
};
# Warn about user accounts with deprecated password hashing schemes
# This does not work when the users and groups are created by
# systemd-sysusers because the users are created too late then.
system.activationScripts.hashes =
if !config.systemd.sysusers.enable && !config.services.userborn.enable then
{
deps = [ "users" ];
text = ''
users=()
while IFS=: read -r user hash _; do
if [[ "$hash" = "$"* && ! "$hash" =~ ^\''$${cryptSchemeIdPatternGroup}\$ ]]; then
users+=("$user")
fi
done </etc/shadow
if (( "''${#users[@]}" )); then
echo "
WARNING: The following user accounts rely on password hashing algorithms
that have been removed. They need to be renewed as soon as possible, as
they do prevent their users from logging in."
printf ' - %s\n' "''${users[@]}"
fi
'';
}
else
""; # keep around for backwards compatibility
# for backwards compatibility
system.activationScripts.hashes = stringAfter [ "users" ] "";
# for backwards compatibility
system.activationScripts.groups = stringAfter [ "users" ] "";

View file

@ -123,7 +123,8 @@ in
options = {
hardware.deviceTree = {
enable = lib.mkOption {
default = pkgs.stdenv.hostPlatform.linux-kernel.DTB or false;
default = config.boot.kernelPackages.kernel.buildDTBs;
defaultText = lib.literalExpression "config.boot.kernelPackages.kernel.buildDTBs";
type = lib.types.bool;
description = ''
Build device tree files. These are used to describe the

View file

@ -111,7 +111,7 @@ with lib;
#!ipxe
# Use the cmdline variable to allow the user to specify custom kernel params
# when chainloading this script from other iPXE scripts like netboot.xyz
kernel ${pkgs.stdenv.hostPlatform.linux-kernel.target} init=${config.system.build.toplevel}/init initrd=initrd ${toString config.boot.kernelParams} ''${cmdline}
kernel ${config.boot.kernelPackages.kernel.target} init=${config.system.build.toplevel}/init initrd=initrd ${toString config.boot.kernelParams} ''${cmdline}
initrd initrd
boot
'';

View file

@ -245,6 +245,7 @@
./programs/iio-hyprland.nix
./programs/immersed.nix
./programs/iotop.nix
./programs/jai-jail.nix
./programs/java.nix
./programs/joycond-cemuhook.nix
./programs/k3b.nix
@ -1033,6 +1034,7 @@
./services/monitoring/grafana-to-ntfy.nix
./services/monitoring/grafana.nix
./services/monitoring/graphite.nix
./services/monitoring/gs1200-exporter.nix
./services/monitoring/hdaps.nix
./services/monitoring/heapster.nix
./services/monitoring/incron.nix
@ -1485,6 +1487,7 @@
./services/scheduling/cron.nix
./services/scheduling/fcron.nix
./services/scheduling/prefect.nix
./services/scheduling/scx-loader.nix
./services/scheduling/scx.nix
./services/search/elasticsearch-curator.nix
./services/search/elasticsearch.nix

View file

@ -81,6 +81,7 @@
LockPersonality = true;
RestrictRealtime = true;
RuntimeDirectory = "clash-verge-rev";
StateDirectory = "clash-verge-service";
ProtectClock = true;
MemoryDenyWriteExecute = true;
RestrictSUIDSGID = true;

View file

@ -9,14 +9,11 @@ function chpwd-osc7-pwd() {
(( ZSH_SUBSHELL )) || osc7-pwd
}
precmd() {
print -Pn "\e]133;A\e\\"
}
function precmd {
if ! builtin zle; then
print -n "\e]133;D\e\\"
fi
print -Pn "\e]133;A\e\\"
}
function preexec {

View file

@ -0,0 +1,45 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.jai-jail;
in
{
options.programs.jai-jail = {
enable = lib.mkEnableOption "jai, a sandbox for AI agents";
package = lib.mkOption {
type = lib.types.package;
default = pkgs.jai-jail;
defaultText = lib.literalExpression "pkgs.jai-jail";
description = "The jai package to use.";
};
};
config = lib.mkIf cfg.enable {
security.wrappers.jai = {
setuid = true;
owner = "root";
group = "root";
source = "${cfg.package}/bin/jai";
};
users.users.jai = {
isSystemUser = true;
group = "jai";
home = "/";
description = "JAI sandbox untrusted user";
};
users.groups.jai = { };
environment.systemPackages = [ cfg.package ];
};
meta.maintainers = with lib.maintainers; [ agentelement ];
}

View file

@ -50,7 +50,7 @@ in
only the hosts listed in {var}`dataDir`/remote_hosts.cfg will be allowed to
connect.
See also: <https://boinc.berkeley.edu/wiki/Controlling_BOINC_remotely#Remote_access>
See also: <https://github.com/BOINC/boinc/wiki/Controlling_BOINC_remotely#remote-access>
'';
};

View file

@ -21,6 +21,12 @@ in
package = lib.mkPackageOption pkgs "espanso" {
example = "pkgs.espanso-wayland";
};
extraPackages = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [ ];
example = lib.literalExpression "with pkgs; [ bash curl python3 ];";
description = "Extra packages to be added to Espanso service path.";
};
};
};
@ -43,6 +49,7 @@ in
Restart = "on-failure";
};
wantedBy = [ "graphical-session.target" ];
path = cfg.extraPackages;
};
environment.systemPackages = [ cfg.package ];

View file

@ -10,7 +10,7 @@ let
in
{
meta.maintainers = with lib.maintainers; [ philipwilk ];
meta.maintainers = with lib.maintainers; [ jadewilk ];
imports = [
(lib.mkRemovedOptionModule [

View file

@ -125,6 +125,12 @@ in
};
users.groups.scanservjs = { };
systemd.tmpfiles.rules = [
"d ${cfg.stateDir}/data 0755 scanservjs scanservjs - -"
"d ${cfg.stateDir}/data/preview 0755 scanservjs scanservjs - -"
"L+ ${cfg.stateDir}/data/preview/default.jpg - - - - ${package}/lib/data/preview/default.jpg"
];
systemd.services.scanservjs = {
description = "scanservjs";
after = [ "network.target" ];

View file

@ -864,12 +864,19 @@ in
networking.firewall.allowedTCPPorts = mkMerge [
(mkIf cfg.openFirewall [ cfg.config.http.server_port ])
(mkIf cfg.openFirewallForComponents
(mkIf cfg.openFirewallForComponents (
# https://www.home-assistant.io/integrations/homekit/#firewall
optionals (useComponent "homekit") [ 21063 ]
# https://www.home-assistant.io/integrations/sonos/#network-requirements
(optionals (useComponent "sonos") [ 1400 ])
)
++ optionals (useComponent "sonos") [ 1400 ]
))
];
networking.firewall.allowedUDPPorts = mkIf cfg.openFirewallForComponents (
# https://www.home-assistant.io/integrations/homekit/#firewall
optionals (useComponent "homekit") [ 5353 ]
);
# symlink the configuration to /etc/home-assistant
environment.etc = mkMerge [
(mkIf (cfg.config != null && !cfg.configWritable) {

View file

@ -304,7 +304,6 @@ in
meta = {
maintainers = with lib.maintainers; [
happysalada
euxane
onny
norpol
];

View file

@ -32,9 +32,14 @@ let
);
getSubModules = elemType.getSubModules;
substSubModules = mod: matrixOf n m (elemType.substSubModules mod);
functor = (lib.defaultFunctor name) // {
wrapped = elemType;
};
functor = (
lib.types.elemTypeFunctor "attrsWith" {
inherit
elemType
name
;
}
);
};
profileModule = lib.types.submodule {

View file

@ -252,12 +252,13 @@ in
in
{
"/" = {
# recommendedProxySettings sets the standard headers (Host, X-Forwarded-*), so
# don't also set them via proxy_set_header in extraConfig below. Nginx would then
# send Host twice and Django rejects it with DisallowedHost. Enabled per-location
# so it works even if the host's global recommendedProxySettings is off.
recommendedProxySettings = true;
extraConfig = ''
proxy_set_header Host $host;
proxy_set_header X-Forwarded-By $server_addr:$server_port;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header CLIENT_IP $remote_addr;
proxy_pass_request_headers on;
@ -272,6 +273,8 @@ in
proxyPass = "http://unix:${unixPath}";
};
"/auth" = {
# same reasoning as "/"; this subrequest also reaches Django
recommendedProxySettings = true;
extraConfig = ''
internal;
proxy_pass_request_body off;

View file

@ -2,188 +2,173 @@
config,
lib,
pkgs,
utils,
...
}:
let
cfg = config.services.llama-cpp;
modelsPresetFile =
if cfg.modelsPreset != null then
pkgs.writeText "llama-models.ini" (lib.generators.toINI { } cfg.modelsPreset)
else
null;
in
{
imports = [
(lib.mkRenamedOptionModule
[ "services" "llama-cpp" "host" ]
[ "services" "llama-cpp" "settings" "host" ]
)
(lib.mkRenamedOptionModule
[ "services" "llama-cpp" "port" ]
[ "services" "llama-cpp" "settings" "port" ]
)
(lib.mkRenamedOptionModule
[ "services" "llama-cpp" "model" ]
[ "services" "llama-cpp" "settings" "model" ]
)
(lib.mkRenamedOptionModule
[ "services" "llama-cpp" "modelsDir" ]
[ "services" "llama-cpp" "settings" "models-dir" ]
)
(lib.mkRemovedOptionModule [ "services" "llama-cpp" "modelsPreset" ] ''
Using a Nix attribute set for configuring model presets is no longer
supported. However, it's possible to use
`services.llama-cpp.settings.models-preset` to provide a path to an INI
file with desired options.
'')
(lib.mkRemovedOptionModule [
"services"
"llama-cpp"
"extraFlags"
] "Use `services.llama-cpp.settings` instead")
];
options = {
services.llama-cpp = {
enable = lib.mkEnableOption "LLaMA C++ server";
enable = lib.mkEnableOption "llama.cpp HTTP server";
package = lib.mkPackageOption pkgs "llama-cpp" { };
model = lib.mkOption {
type = lib.types.nullOr lib.types.path;
example = "/models/mistral-instruct-7b/ggml-model-q4_0.gguf";
description = "Model path.";
default = null;
};
modelsDir = lib.mkOption {
type = lib.types.nullOr lib.types.path;
example = "/models/";
description = "Models directory.";
default = null;
};
modelsPreset = lib.mkOption {
type = lib.types.nullOr (lib.types.attrsOf lib.types.attrs);
default = null;
description = ''
Models preset configuration as a Nix attribute set.
This is converted to an INI file and passed to llama-server via --model-preset.
See llama-server documentation for available options.
'';
example = lib.literalExpression ''
{
"Qwen3-Coder-Next" = {
hf-repo = "unsloth/Qwen3-Coder-Next-GGUF";
hf-file = "Qwen3-Coder-Next-UD-Q4_K_XL.gguf";
alias = "unsloth/Qwen3-Coder-Next";
fit = "on";
seed = "3407";
temp = "1.0";
top-p = "0.95";
min-p = "0.01";
top-k = "40";
jinja = "on";
settings = lib.mkOption {
type = lib.types.submodule {
freeformType = lib.types.attrs;
options = {
host = lib.mkOption {
type = lib.types.str;
default = "127.0.0.1";
example = "0.0.0.0";
description = ''
IP address on which the server should listen on.
'';
};
}
port = lib.mkOption {
type = lib.types.port;
default = 8080;
example = 1337;
description = ''
Port on which the server should listen on.
'';
};
};
};
default = { };
example = {
host = "0.0.0.0";
port = 1337;
model = "/mnt/llms/Foo3.6-27B-UD-Q4_K_XL.gguf";
ctx-size = 252144;
temp = 0.6;
top-k = 20;
top-p = 0.95;
batch-size = 512;
ubatch-size = 256;
spec-type = "draft-mtp";
spec-draft-n-max = 2;
flash-attn = "on";
};
description = ''
Command-line arguments for `llama-server`.
See <https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md>
for the full list of options.
'';
};
extraFlags = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = "Extra flags passed to llama-cpp-server.";
example = [
"-c"
"4096"
"-ngl"
"32"
"--numa"
"numactl"
];
default = [ ];
};
host = lib.mkOption {
type = lib.types.str;
default = "127.0.0.1";
example = "0.0.0.0";
description = "IP address the LLaMA C++ server listens on.";
};
port = lib.mkOption {
type = lib.types.port;
default = 8080;
description = "Listen port for LLaMA C++ server.";
};
openFirewall = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Open ports in the firewall for LLaMA C++ server.";
description = ''
Open ports in the firewall for the server.
'';
};
};
};
config = lib.mkIf cfg.enable {
systemd.services.llama-cpp = {
description = "LLaMA C++ server";
description = "llama.cpp HTTP server";
wants = [ "network.target" ];
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "idle";
KillSignal = "SIGINT";
ExecStart = toString [
(lib.getExe' cfg.package "llama-server")
(lib.cli.toCommandLine (optionName: {
option = if builtins.stringLength optionName > 1 then "--${optionName}" else "-${optionName}";
sep = " ";
explicitBool = false;
formatArg = lib.generators.mkValueStringDefault { };
}) cfg.settings)
];
ExecReload = "${lib.getExe' pkgs.coreutils "kill"} -HUP $MAINPID";
Restart = "on-failure";
RestartSec = 300;
DynamicUser = true;
StateDirectory = "llama-cpp";
CacheDirectory = "llama-cpp";
WorkingDirectory = "/var/lib/llama-cpp";
Environment = [ "LLAMA_CACHE=/var/cache/llama-cpp" ];
ExecStart =
let
args = [
"--host"
cfg.host
"--port"
(toString cfg.port)
]
++ lib.optionals (cfg.model != null) [
"-m"
cfg.model
]
++ lib.optionals (cfg.modelsDir != null) [
"--models-dir"
cfg.modelsDir
]
++ lib.optionals (cfg.modelsPreset != null) [
"--models-preset"
modelsPresetFile
]
++ cfg.extraFlags;
in
"${cfg.package}/bin/llama-server ${utils.escapeSystemdExecArgs args}";
Restart = "on-failure";
RestartSec = 300;
# for GPU acceleration
PrivateDevices = false;
# hardening
DynamicUser = true;
CapabilityBoundingSet = "";
AmbientCapabilities = [ "" ];
CapabilityBoundingSet = [ "" ];
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateDevices = false; # Required for GPU support.
PrivateMounts = true;
PrivateTmp = true;
PrivateUsers = true;
ProcSubset = "pid";
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProtectSystem = "strict";
RemoveIPC = true;
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
NoNewPrivileges = true;
PrivateMounts = true;
PrivateTmp = true;
PrivateUsers = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectSystem = "strict";
MemoryDenyWriteExecute = true;
LockPersonality = true;
RemoveIPC = true;
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
SystemCallErrorNumber = "EPERM";
SystemCallFilter = [
"@system-service"
"~@privileged"
];
SystemCallErrorNumber = "EPERM";
ProtectProc = "invisible";
ProtectHostname = true;
ProcSubset = "pid";
};
};
networking.firewall = lib.mkIf cfg.openFirewall {
allowedTCPPorts = [ cfg.port ];
};
networking.firewall.allowedTCPPorts = lib.optional cfg.openFirewall cfg.port;
};
meta.maintainers = with lib.maintainers; [ newam ];
meta.maintainers = with lib.maintainers; [
azahi
newam
];
}

View file

@ -146,5 +146,5 @@ in
};
};
meta.maintainers = with lib.maintainers; [ euxane ];
meta.maintainers = with lib.maintainers; [ martinetd ];
}

View file

@ -0,0 +1,95 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.gs1200-exporter;
in
{
meta.maintainers = with lib.maintainers; [ DerGrumpf ];
options.services.gs1200-exporter = {
enable = lib.mkEnableOption "gs1200-exporter";
address = lib.mkOption {
type = lib.types.str;
description = "IP address or hostname of the GS1200 switch.";
example = "192.168.1.3";
};
port = lib.mkOption {
type = lib.types.port;
default = 9934;
description = "Port on which to expose Prometheus metrics.";
};
passwordFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = ''
Path to a file containing the password to log in to the GS1200 web interface.
This is the recommended option as it avoids storing the password in the Nix store.
Compatible with sops-nix and agenix.
'';
example = "/run/secrets/gs1200-password";
};
debug = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable debug logging. Logs are accessible via journalctl -u gs1200-exporter.";
};
verbose = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable verbose logging. Logs are accessible via journalctl -u gs1200-exporter.";
};
json = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Output logs in JSON format. Logs are accessible via journalctl -u gs1200-exporter.";
};
};
config = lib.mkIf cfg.enable {
systemd.services.gs1200-exporter = {
description = "Prometheus exporter for Zyxel GS1200 switches";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
DynamicUser = true;
Restart = "always";
# Hardening
NoNewPrivileges = true;
PrivateTmp = true;
ProtectSystem = "strict";
ProtectHome = true;
CapabilityBoundingSet = "";
};
script =
let
args = lib.concatStringsSep " " (
[
"--address ${cfg.address}"
"--port ${toString cfg.port}"
]
++ lib.optional cfg.debug "--debug"
++ lib.optional cfg.verbose "--verbose"
++ lib.optional cfg.json "--json"
);
in
''
export GS1200_PASSWORD=$(cat ${cfg.passwordFile})
exec ${lib.getExe pkgs.gs1200-exporter} ${args}
'';
};
};
}

View file

@ -1,709 +0,0 @@
defmodule Provision do
alias Domain.{Repo, Accounts, Auth, Actors, Resources, Tokens, Gateways, Relays, Policies}
require Logger
# UUID Mapping handling
defmodule UuidMapping do
@mapping_file "provision-uuids.json"
# Loads the mapping from file
def load do
mappings = case File.read(@mapping_file) do
{:ok, content} ->
case Jason.decode(content) do
{:ok, mapping} -> mapping
_ -> %{"accounts" => %{}}
end
_ -> %{"accounts" => %{}}
end
Process.put(:uuid_mappings, mappings)
mappings
end
# Saves the current mapping (defaulting to the one in the process dictionary)
def save(mapping \\ Process.get(:uuid_mappings)) do
File.write!(@mapping_file, Jason.encode!(mapping))
end
# Retrieves the account-level mapping from a given mapping (or from Process)
def get_account(mapping \\ Process.get(:uuid_mappings), account_slug) do
get_in(mapping, ["accounts", account_slug]) || %{}
end
# Retrieves the entity mapping for a specific account and type
def get_entities(mapping \\ Process.get(:uuid_mappings), account_slug, type) do
get_in(mapping, ["accounts", account_slug, type]) || %{}
end
# Retrieves an entity mapping for a specific account, type and external_id
def get_entity(mapping \\ Process.get(:uuid_mappings), account_slug, type, external_id) do
get_in(mapping, ["accounts", account_slug, type, external_id])
end
# Updates (or creates) the account UUID mapping and stores it in the process dictionary.
def update_account(account_slug, uuid) do
mapping = Process.get(:uuid_mappings) || load()
mapping = ensure_account_exists(mapping, account_slug)
mapping = put_in(mapping, ["accounts", account_slug, "id"], uuid)
Process.put(:uuid_mappings, mapping)
mapping
end
# Ensures that the given account exists in the mapping.
def ensure_account_exists(mapping, account_slug) do
if not Map.has_key?(mapping["accounts"], account_slug) do
put_in(mapping, ["accounts", account_slug], %{})
else
mapping
end
end
# Updates (or creates) the mapping for entities of a given type for the account.
def update_entities(account_slug, type, new_entries) do
mapping = Process.get(:uuid_mappings) || load()
mapping = ensure_account_exists(mapping, account_slug)
current = get_entities(mapping, account_slug, type)
mapping = put_in(mapping, ["accounts", account_slug, type], Map.merge(current, new_entries))
Process.put(:uuid_mappings, mapping)
mapping
end
# Removes an entire account from the mapping.
def remove_account(account_slug) do
mapping = Process.get(:uuid_mappings) || load()
mapping = update_in(mapping, ["accounts"], fn accounts ->
Map.delete(accounts, account_slug)
end)
Process.put(:uuid_mappings, mapping)
mapping
end
# Removes a specific entity mapping for the account.
def remove_entity(account_slug, type, key) do
mapping = Process.get(:uuid_mappings) || load()
mapping = update_in(mapping, ["accounts", account_slug, type], fn entities ->
Map.delete(entities || %{}, key)
end)
Process.put(:uuid_mappings, mapping)
mapping
end
end
defp resolve_references(value) when is_map(value) do
Enum.into(value, %{}, fn {k, v} -> {k, resolve_references(v)} end)
end
defp resolve_references(value) when is_list(value) do
Enum.map(value, &resolve_references/1)
end
defp resolve_references(value) when is_binary(value) do
Regex.replace(~r/\{env:([^}]+)\}/, value, fn _, var ->
System.get_env(var) || raise "Environment variable #{var} not set"
end)
end
defp resolve_references(value), do: value
defp atomize_keys(map) when is_map(map) do
Enum.into(map, %{}, fn {k, v} ->
{
if(is_binary(k), do: String.to_atom(k), else: k),
if(is_map(v), do: atomize_keys(v), else: v)
}
end)
end
defp cleanup_account(uuid) do
case Accounts.fetch_account_by_id_or_slug(uuid) do
{:ok, value} when value.deleted_at == nil ->
Logger.info("Deleting removed account #{value.slug}")
value |> Ecto.Changeset.change(%{ deleted_at: DateTime.utc_now() }) |> Repo.update!()
_ -> :ok
end
end
defp cleanup_actor(uuid, subject) do
case Actors.fetch_actor_by_id(uuid, subject) do
{:ok, value} ->
Logger.info("Deleting removed actor #{value.name}")
{:ok, _} = Actors.delete_actor(value, subject)
_ -> :ok
end
end
defp cleanup_provider(uuid, subject) do
case Auth.fetch_provider_by_id(uuid, subject) do
{:ok, value} ->
Logger.info("Deleting removed provider #{value.name}")
{:ok, _} = Auth.delete_provider(value, subject)
_ -> :ok
end
end
defp cleanup_gateway_group(uuid, subject) do
case Gateways.fetch_group_by_id(uuid, subject) do
{:ok, value} ->
Logger.info("Deleting removed gateway group #{value.name}")
{:ok, _} = Gateways.delete_group(value, subject)
_ -> :ok
end
end
defp cleanup_relay_group(uuid, subject) do
case Relays.fetch_group_by_id(uuid, subject) do
{:ok, value} ->
Logger.info("Deleting removed relay group #{value.name}")
{:ok, _} = Relays.delete_group(value, subject)
_ -> :ok
end
end
defp cleanup_actor_group(uuid, subject) do
case Actors.fetch_group_by_id(uuid, subject) do
{:ok, value} ->
Logger.info("Deleting removed actor group #{value.name}")
{:ok, _} = Actors.delete_group(value, subject)
_ -> :ok
end
end
# Fetch resource by uuid, but follow the chain of replacements if any
defp fetch_resource(uuid, subject) do
case Resources.fetch_resource_by_id(uuid, subject) do
{:ok, resource} when resource.replaced_by_resource_id != nil -> fetch_resource(resource.replaced_by_resource_id, subject)
v -> v
end
end
defp cleanup_resource(uuid, subject) do
case fetch_resource(uuid, subject) do
{:ok, value} when value.deleted_at == nil ->
Logger.info("Deleting removed resource #{value.name}")
{:ok, _} = Resources.delete_resource(value, subject)
_ -> :ok
end
end
# Fetch policy by uuid, but follow the chain of replacements if any
defp fetch_policy(uuid, subject) do
case Policies.fetch_policy_by_id(uuid, subject) do
{:ok, policy} when policy.replaced_by_policy_id != nil -> fetch_policy(policy.replaced_by_policy_id, subject)
v -> v
end
end
defp cleanup_policy(uuid, subject) do
case fetch_policy(uuid, subject) do
{:ok, value} when value.deleted_at == nil ->
Logger.info("Deleting removed policy #{value.description}")
{:ok, _} = Policies.delete_policy(value, subject)
_ -> :ok
end
end
defp cleanup_entity_type(account_slug, entity_type, cleanup_fn, temp_admin_subject) do
# Get mapping for this entity type
existing_entities = UuidMapping.get_entities(account_slug, entity_type)
# Get current entities from account data
current_entities = Process.get(:current_entities)
# Determine which ones to remove
removed_entity_ids = Map.keys(existing_entities) -- (current_entities[entity_type] || [])
# Process each entity to remove
Enum.each(removed_entity_ids, fn entity_id ->
case existing_entities[entity_id] do
nil -> :ok
uuid ->
cleanup_fn.(uuid, temp_admin_subject)
UuidMapping.remove_entity(account_slug, entity_type, entity_id)
end
end)
end
defp collect_current_entities(account_data) do
%{
"actors" => Map.keys(account_data["actors"] || %{}),
"providers" => Map.keys(account_data["auth"] || %{}),
"gateway_groups" => Map.keys(account_data["gatewayGroups"] || %{}),
"relay_groups" => Map.keys(account_data["relayGroups"] || %{}),
"actor_groups" => Map.keys(account_data["groups"] || %{}) ++ ["everyone"],
"resources" => Map.keys(account_data["resources"] || %{}),
"policies" => Map.keys(account_data["policies"] || %{})
}
end
defp nil_if_deleted_or_not_found(value) do
case value do
nil -> nil
{:error, :not_found} -> nil
{:ok, value} when value.deleted_at != nil -> nil
v -> v
end
end
defp create_temp_admin(account, email_provider) do
temp_admin_actor_email = "firezone-provision@localhost.local"
temp_admin_actor_context = %Auth.Context{
type: :browser,
user_agent: "Unspecified/0.0",
remote_ip: {127, 0, 0, 1},
remote_ip_location_region: "N/A",
remote_ip_location_city: "N/A",
remote_ip_location_lat: 0.0,
remote_ip_location_lon: 0.0
}
{:ok, temp_admin_actor} =
Actors.create_actor(account, %{
type: :account_admin_user,
name: "Provisioning"
})
{:ok, temp_admin_actor_email_identity} =
Auth.create_identity(temp_admin_actor, email_provider, %{
provider_identifier: temp_admin_actor_email,
provider_identifier_confirmation: temp_admin_actor_email
})
{:ok, temp_admin_actor_token} =
Auth.create_token(temp_admin_actor_email_identity, temp_admin_actor_context, "temporarynonce", DateTime.utc_now() |> DateTime.add(1, :hour))
{:ok, temp_admin_subject} =
Auth.build_subject(temp_admin_actor_token, temp_admin_actor_context)
{temp_admin_subject, temp_admin_actor, temp_admin_actor_email_identity, temp_admin_actor_token}
end
defp cleanup_temp_admin(temp_admin_actor, temp_admin_actor_email_identity, temp_admin_actor_token, subject) do
Logger.info("Cleaning up temporary admin actor")
{:ok, _} = Tokens.delete_token(temp_admin_actor_token, subject)
{:ok, _} = Auth.delete_identity(temp_admin_actor_email_identity, subject)
{:ok, _} = Actors.delete_actor(temp_admin_actor, subject)
end
def provision() do
Logger.info("Starting provisioning")
# Load desired state
json_file = "provision-state.json"
{:ok, raw_json} = File.read(json_file)
{:ok, %{"accounts" => accounts}} = Jason.decode(raw_json)
accounts = resolve_references(accounts)
# Load existing UUID mappings into the process dictionary.
UuidMapping.load()
# Clean up removed accounts first
current_account_slugs = Map.keys(accounts)
existing_accounts = Map.keys(Process.get(:uuid_mappings)["accounts"])
removed_accounts = existing_accounts -- current_account_slugs
Enum.each(removed_accounts, fn slug ->
if uuid = get_in(Process.get(:uuid_mappings), ["accounts", slug, "id"]) do
cleanup_account(uuid)
# Remove the account from the UUID mapping.
UuidMapping.remove_account(slug)
end
end)
multi = Enum.reduce(accounts, Ecto.Multi.new(), fn {slug, account_data}, multi ->
account_attrs = atomize_keys(%{
name: account_data["name"],
slug: slug,
features: Map.get(account_data, "features", %{}),
metadata: Map.get(account_data, "metadata", %{}),
limits: Map.get(account_data, "limits", %{})
})
multi = multi
|> Ecto.Multi.run({:account, slug}, fn repo, _changes ->
case Accounts.fetch_account_by_id_or_slug(slug) do
{:ok, acc} ->
Logger.info("Updating existing account #{slug}")
updated_acc = acc |> Ecto.Changeset.change(account_attrs) |> repo.update!()
{:ok, {:existing, updated_acc}}
_ ->
Logger.info("Creating new account #{slug}")
{:ok, account} = Accounts.create_account(account_attrs)
Logger.info("Creating internet gateway group")
{:ok, internet_site} = Gateways.create_internet_group(account)
Logger.info("Creating internet resource")
{:ok, _internet_resource} = Resources.create_internet_resource(account, internet_site)
# Store mapping of slug to UUID
UuidMapping.update_account(slug, account.id)
{:ok, {:new, account}}
end
end)
|> Ecto.Multi.run({:everyone_group, slug}, fn _repo, changes ->
case Map.get(changes, {:account, slug}) do
{:new, account} ->
Logger.info("Creating everyone group for new account")
{:ok, actor_group} = Actors.create_managed_group(account, %{name: "Everyone"})
UuidMapping.update_entities(slug, "actor_groups", %{"everyone" => actor_group.id})
{:ok, actor_group}
{:existing, _account} ->
{:ok, :skipped}
end
end)
|> Ecto.Multi.run({:email_provider, slug}, fn _repo, changes ->
case Map.get(changes, {:account, slug}) do
{:new, account} ->
Logger.info("Creating default email provider for new account")
Auth.create_provider(account, %{name: "Email", adapter: :email, adapter_config: %{}})
{:existing, account} ->
Auth.Provider.Query.not_disabled()
|> Auth.Provider.Query.by_adapter(:email)
|> Auth.Provider.Query.by_account_id(account.id)
|> Repo.fetch(Auth.Provider.Query, [])
end
end)
|> Ecto.Multi.run({:temp_admin, slug}, fn _repo, changes ->
{_, account} = changes[{:account, slug}]
email_provider = changes[{:email_provider, slug}]
{:ok, create_temp_admin(account, email_provider)}
end)
# Clean up removed entities for this account after we have an admin subject
multi = multi
|> Ecto.Multi.run({:cleanup_entities, slug}, fn _repo, changes ->
{temp_admin_subject, _, _, _} = changes[{:temp_admin, slug}]
# Store current entities in process dictionary for our helper function
current_entities = collect_current_entities(account_data)
Process.put(:current_entities, current_entities)
# Define entity types and their cleanup functions
entity_types = [
{"actors", &cleanup_actor/2},
{"providers", &cleanup_provider/2},
{"gateway_groups", &cleanup_gateway_group/2},
{"relay_groups", &cleanup_relay_group/2},
{"actor_groups", &cleanup_actor_group/2},
{"resources", &cleanup_resource/2},
{"policies", &cleanup_policy/2}
]
# Clean up each entity type
Enum.each(entity_types, fn {entity_type, cleanup_fn} ->
cleanup_entity_type(slug, entity_type, cleanup_fn, temp_admin_subject)
end)
{:ok, :cleaned}
end)
# Create or update actors
multi = Enum.reduce(account_data["actors"] || %{}, multi, fn {external_id, actor_data}, multi ->
actor_attrs = atomize_keys(%{
name: actor_data["name"],
type: String.to_atom(actor_data["type"])
})
Ecto.Multi.run(multi, {:actor, slug, external_id}, fn _repo, changes ->
{_, account} = changes[{:account, slug}]
{temp_admin_subject, _, _, _} = changes[{:temp_admin, slug}]
uuid = UuidMapping.get_entity(slug, "actors", external_id)
case uuid && Actors.fetch_actor_by_id(uuid, temp_admin_subject) |> nil_if_deleted_or_not_found() do
nil ->
Logger.info("Creating new actor #{actor_data["name"]}")
{:ok, actor} = Actors.create_actor(account, actor_attrs)
# Update the mapping without manually handling Process.get/put.
UuidMapping.update_entities(slug, "actors", %{external_id => actor.id})
{:ok, {:new, actor}}
{:ok, existing_actor} ->
Logger.info("Updating existing actor #{actor_data["name"]}")
{:ok, updated_act} = Actors.update_actor(existing_actor, actor_attrs, temp_admin_subject)
{:ok, {:existing, updated_act}}
end
end)
|> Ecto.Multi.run({:actor_identity, slug, external_id}, fn repo, changes ->
email_provider = changes[{:email_provider, slug}]
case Map.get(changes, {:actor, slug, external_id}) do
{:new, actor} ->
Logger.info("Creating actor email identity")
Auth.create_identity(actor, email_provider, %{
provider_identifier: actor_data["email"],
provider_identifier_confirmation: actor_data["email"]
})
{:existing, actor} ->
Logger.info("Updating actor email identity")
{:ok, identity} = Auth.Identity.Query.not_deleted()
|> Auth.Identity.Query.by_actor_id(actor.id)
|> Auth.Identity.Query.by_provider_id(email_provider.id)
|> Repo.fetch(Auth.Identity.Query, [])
{:ok, identity |> Ecto.Changeset.change(%{
provider_identifier: actor_data["email"]
}) |> repo.update!()}
end
end)
end)
# Create or update providers
multi = Enum.reduce(account_data["auth"] || %{}, multi, fn {external_id, provider_data}, multi ->
Ecto.Multi.run(multi, {:provider, slug, external_id}, fn repo, changes ->
provider_attrs = %{
name: provider_data["name"],
adapter: String.to_atom(provider_data["adapter"]),
adapter_config: provider_data["adapter_config"]
}
{_, account} = changes[{:account, slug}]
{temp_admin_subject, _, _, _} = changes[{:temp_admin, slug}]
uuid = UuidMapping.get_entity(slug, "providers", external_id)
case uuid && Auth.fetch_provider_by_id(uuid, temp_admin_subject) |> nil_if_deleted_or_not_found() do
nil ->
Logger.info("Creating new provider #{provider_data["name"]}")
{:ok, provider} = Auth.create_provider(account, provider_attrs)
UuidMapping.update_entities(slug, "providers", %{external_id => provider.id})
{:ok, provider}
{:ok, existing} ->
Logger.info("Updating existing provider #{provider_data["name"]}")
{:ok, existing |> Ecto.Changeset.change(provider_attrs) |> repo.update!()}
end
end)
end)
# Create or update gateway_groups
multi = Enum.reduce(account_data["gatewayGroups"] || %{}, multi, fn {external_id, gateway_group_data}, multi ->
Ecto.Multi.run(multi, {:gateway_group, slug, external_id}, fn _repo, changes ->
gateway_group_attrs = %{
name: gateway_group_data["name"],
tokens: [%{}]
}
{_, account} = changes[{:account, slug}]
{temp_admin_subject, _, _, _} = changes[{:temp_admin, slug}]
uuid = UuidMapping.get_entity(slug, "gateway_groups", external_id)
case uuid && Gateways.fetch_group_by_id(uuid, temp_admin_subject) |> nil_if_deleted_or_not_found() do
nil ->
Logger.info("Creating new gateway group #{gateway_group_data["name"]}")
gateway_group = account
|> Gateways.Group.Changeset.create(gateway_group_attrs, temp_admin_subject)
|> Repo.insert!()
UuidMapping.update_entities(slug, "gateway_groups", %{external_id => gateway_group.id})
{:ok, gateway_group}
{:ok, existing} ->
# Nothing to update
{:ok, existing}
end
end)
end)
# Create or update relay_groups
multi = Enum.reduce(account_data["relayGroups"] || %{}, multi, fn {external_id, relay_group_data}, multi ->
Ecto.Multi.run(multi, {:relay_group, slug, external_id}, fn _repo, changes ->
relay_group_attrs = %{
name: relay_group_data["name"]
}
{temp_admin_subject, _, _, _} = changes[{:temp_admin, slug}]
uuid = UuidMapping.get_entity(slug, "relay_groups", external_id)
existing_relay_group = uuid && Relays.fetch_group_by_id(uuid, temp_admin_subject)
case existing_relay_group do
v when v in [nil, {:error, :not_found}] ->
Logger.info("Creating new relay group #{relay_group_data["name"]}")
{:ok, relay_group} = Relays.create_group(relay_group_attrs, temp_admin_subject)
UuidMapping.update_entities(slug, "relay_groups", %{external_id => relay_group.id})
{:ok, relay_group}
{:ok, existing} ->
# Nothing to update
{:ok, existing}
end
end)
end)
# Create or update actor_groups
multi = Enum.reduce(account_data["groups"] || %{}, multi, fn {external_id, actor_group_data}, multi ->
Ecto.Multi.run(multi, {:actor_group, slug, external_id}, fn _repo, changes ->
actor_group_attrs = %{
name: actor_group_data["name"],
type: :static
}
{temp_admin_subject, _, _, _} = changes[{:temp_admin, slug}]
uuid = UuidMapping.get_entity(slug, "actor_groups", external_id)
case uuid && Actors.fetch_group_by_id(uuid, temp_admin_subject) |> nil_if_deleted_or_not_found() do
nil ->
Logger.info("Creating new actor group #{actor_group_data["name"]}")
{:ok, actor_group} = Actors.create_group(actor_group_attrs, temp_admin_subject)
UuidMapping.update_entities(slug, "actor_groups", %{external_id => actor_group.id})
{:ok, actor_group}
{:ok, existing} ->
# Nothing to update
{:ok, existing}
end
end)
|> Ecto.Multi.run({:actor_group_members, slug, external_id}, fn repo, changes ->
{_, account} = changes[{:account, slug}]
group_uuid = UuidMapping.get_entity(slug, "actor_groups", external_id)
memberships =
Actors.Membership.Query.all()
|> Actors.Membership.Query.by_group_id(group_uuid)
|> Actors.Membership.Query.returning_all()
|> Repo.all()
existing_members = Enum.map(memberships, fn membership -> membership.actor_id end)
desired_members = Enum.map(actor_group_data["members"] || [], fn member ->
uuid = UuidMapping.get_entity(slug, "actors", member)
if uuid == nil do
raise "Cannot find provisioned actor #{member} to add to group"
end
uuid
end)
missing_members = desired_members -- existing_members
untracked_members = existing_members -- desired_members
Logger.info("Updating members for actor group #{external_id}")
Enum.each(missing_members || [], fn actor_uuid ->
Logger.info("Adding member #{external_id}")
Actors.Membership.Changeset.upsert(account.id, %Actors.Membership{}, %{
group_id: group_uuid,
actor_id: actor_uuid
})
|> repo.insert!()
end)
if actor_group_data["forceMembers"] == true do
# Remove untracked members
to_delete = Enum.map(untracked_members, fn actor_uuid -> {group_uuid, actor_uuid} end)
if to_delete != [] do
Actors.Membership.Query.by_group_id_and_actor_id({:in, to_delete})
|> repo.delete_all()
end
end
{:ok, nil}
end)
end)
# Create or update resources
multi = Enum.reduce(account_data["resources"] || %{}, multi, fn {external_id, resource_data}, multi ->
Ecto.Multi.run(multi, {:resource, slug, external_id}, fn _repo, changes ->
resource_attrs = %{
type: String.to_atom(resource_data["type"]),
name: resource_data["name"],
address: resource_data["address"],
address_description: resource_data["address_description"],
connections: Enum.map(resource_data["gatewayGroups"] || [], fn group ->
%{gateway_group_id: UuidMapping.get_entity(slug, "gateway_groups", group)}
end),
filters: Enum.map(resource_data["filters"] || [], fn filter ->
%{
ports: filter["ports"] || [],
protocol: String.to_atom(filter["protocol"])
}
end)
}
{temp_admin_subject, _, _, _} = changes[{:temp_admin, slug}]
uuid = UuidMapping.get_entity(slug, "resources", external_id)
case uuid && fetch_resource(uuid, temp_admin_subject) |> nil_if_deleted_or_not_found() do
nil ->
Logger.info("Creating new resource #{resource_data["name"]}")
{:ok, resource} = Resources.create_resource(resource_attrs, temp_admin_subject)
UuidMapping.update_entities(slug, "resources", %{external_id => resource.id})
{:ok, resource}
{:ok, existing} ->
existing = Repo.preload(existing, :connections)
Logger.info("Updating existing resource #{resource_data["name"]}")
only_updated_attrs = resource_attrs
|> Enum.reject(fn {key, value} ->
case key do
# Compare connections by gateway_group_id only
:connections -> value == Enum.map(existing.connections || [], fn conn -> Map.take(conn, [:gateway_group_id]) end)
# Compare filters by ports and protocol only
:filters -> value == Enum.map(existing.filters || [], fn filter -> Map.take(filter, [:ports, :protocol]) end)
_ -> Map.get(existing, key) == value
end
end)
|> Enum.into(%{})
if only_updated_attrs == %{} do
{:ok, existing}
else
resource = case existing |> Resources.update_resource(resource_attrs, temp_admin_subject) do
{:replaced, _old, new} ->
UuidMapping.update_entities(slug, "resources", %{external_id => new.id})
new
{:updated, value} -> value
x -> x
end
{:ok, resource}
end
end
end)
end)
# Create or update policies
multi = Enum.reduce(account_data["policies"] || %{}, multi, fn {external_id, policy_data}, multi ->
Ecto.Multi.run(multi, {:policy, slug, external_id}, fn _repo, changes ->
policy_attrs = %{
description: policy_data["description"],
actor_group_id: UuidMapping.get_entity(slug, "actor_groups", policy_data["group"]),
resource_id: UuidMapping.get_entity(slug, "resources", policy_data["resource"])
}
{temp_admin_subject, _, _, _} = changes[{:temp_admin, slug}]
uuid = UuidMapping.get_entity(slug, "policies", external_id)
case uuid && fetch_policy(uuid, temp_admin_subject) |> nil_if_deleted_or_not_found() do
nil ->
Logger.info("Creating new policy #{policy_data["name"]}")
{:ok, policy} = Policies.create_policy(policy_attrs, temp_admin_subject)
UuidMapping.update_entities(slug, "policies", %{external_id => policy.id})
{:ok, policy}
{:ok, existing} ->
Logger.info("Updating existing policy #{policy_data["name"]}")
only_updated_attrs = policy_attrs
|> Enum.reject(fn {key, value} -> Map.get(existing, key) == value end)
|> Enum.into(%{})
if only_updated_attrs == %{} do
{:ok, existing}
else
policy = case existing |> Policies.update_policy(policy_attrs, temp_admin_subject) do
{:replaced, _old, new} ->
UuidMapping.update_entities(slug, "policies", %{external_id => new.id})
new
{:updated, value} -> value
x -> x
end
{:ok, policy}
end
end
end)
end)
# Clean up temporary admin after all operations
multi |> Ecto.Multi.run({:cleanup_temp_admin, slug}, fn _repo, changes ->
{temp_admin_subject, temp_admin_actor, temp_admin_actor_email_identity, temp_admin_actor_token} =
changes[{:temp_admin, slug}]
cleanup_temp_admin(temp_admin_actor, temp_admin_actor_email_identity, temp_admin_actor_token, temp_admin_subject)
{:ok, :cleaned}
end)
end)
|> Ecto.Multi.run({:save_state}, fn _repo, _changes ->
# Save all UUID mappings to disk.
UuidMapping.save()
{:ok, :saved}
end)
case Repo.transaction(multi) do
{:ok, _result} ->
Logger.info("Provisioning completed successfully")
{:error, step, reason, _changes} ->
Logger.error("Provisioning failed at step #{inspect(step)}, no changes were applied: #{inspect(reason)}")
end
end
end
Provision.provision()

View file

@ -9,12 +9,8 @@ let
attrNames
boolToString
concatLines
concatLists
concatMapAttrs
concatStringsSep
filterAttrs
filterAttrsRecursive
flip
forEach
getExe
isBool
@ -26,16 +22,11 @@ let
mkMerge
mkOption
mkPackageOption
optionalAttrs
optionalString
recursiveUpdate
subtractLists
toUpper
types
;
cfg = config.services.firezone.server;
jsonFormat = pkgs.formats.json { };
availableAuthAdapters = [
"email"
"openid_connect"
@ -47,28 +38,6 @@ let
"jumpcloud"
];
typePortRange =
types.coercedTo types.port
(x: {
from = x;
to = x;
})
(
types.submodule {
options = {
from = mkOption {
type = types.port;
description = "The start of the port range, inclusive.";
};
to = mkOption {
type = types.port;
description = "The end of the port range, inclusive.";
};
};
}
);
# All non-secret environment variables or the given component
collectEnvironment =
component:
@ -119,29 +88,6 @@ let
)
);
provisionStateJson =
let
# Convert clientSecretFile options into the real counterpart
augmentedAccounts = flip mapAttrs cfg.provision.accounts (
accountName: account:
account
// {
auth = flip mapAttrs account.auth (
authName: auth:
recursiveUpdate auth (
optionalAttrs (auth.adapter_config.clientSecretFile != null) {
adapter_config.client_secret = "{env:AUTH_CLIENT_SECRET_${toUpper accountName}_${toUpper authName}}";
}
)
);
}
);
in
jsonFormat.generate "provision-state.json" {
# Do not include any clientSecretFile attributes in the resulting json
accounts = filterAttrsRecursive (k: _: k != "clientSecretFile") augmentedAccounts;
};
commonServiceConfig = {
AmbientCapabilities = [ ];
CapabilityBoundingSet = [ ];
@ -222,6 +168,12 @@ let
};
in
{
imports = [
(lib.mkRemovedOptionModule [ "services" "firezone" "server" "provision" ] ''
Firezone provisioning support has been removed due to outsized maintenance efforts. See https://github.com/NixOS/nixpkgs/pull/529428.
'')
];
options.services.firezone.server = {
enable = mkEnableOption "all Firezone components";
enableLocalDB = mkEnableOption "a local postgresql database for Firezone";
@ -521,393 +473,9 @@ in
description = "A list of trusted proxies";
};
};
provision = {
enable = mkEnableOption "provisioning of the Firezone domain server";
accounts = mkOption {
type = types.attrsOf (
types.submodule {
freeformType = jsonFormat.type;
options = {
name = mkOption {
type = types.str;
description = "The account name";
example = "My Organization";
};
features =
let
mkFeatureOption =
name: default:
mkOption {
type = types.bool;
inherit default;
description = "Whether to enable the `${name}` feature for this account.";
};
in
{
policy_conditions = mkFeatureOption "policy_conditions" true;
multi_site_resources = mkFeatureOption "multi_site_resources" true;
traffic_filters = mkFeatureOption "traffic_filters" true;
self_hosted_relays = mkFeatureOption "self_hosted_relays" true;
idp_sync = mkFeatureOption "idp_sync" true;
rest_api = mkFeatureOption "rest_api" true;
internet_resource = mkFeatureOption "internet_resource" true;
};
actors = mkOption {
type = types.attrsOf (
types.submodule {
options = {
type = mkOption {
type = types.enum [
"account_admin_user"
"account_user"
"service_account"
"api_client"
];
description = "The account type";
};
name = mkOption {
type = types.str;
description = "The name of this actor";
};
email = mkOption {
type = types.str;
description = "The email address used to authenticate as this account";
};
};
}
);
default = { };
example = {
admin = {
type = "account_admin_user";
name = "Admin";
email = "admin@myorg.example.com";
};
};
description = ''
All actors (users) to provision. The attribute name will only
be used to track the actor and does not have any significance
for Firezone.
'';
};
auth = mkOption {
type = types.attrsOf (
types.submodule {
freeformType = jsonFormat.type;
options = {
name = mkOption {
type = types.str;
description = "The name of this authentication provider";
};
adapter = mkOption {
type = types.enum availableAuthAdapters;
description = "The auth adapter type";
};
adapter_config.clientSecretFile = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
A file containing a the client secret for an openid_connect adapter.
You only need to set this if this is an openid_connect provider.
'';
};
};
}
);
default = { };
example = {
myoidcprovider = {
adapter = "openid_connect";
adapter_config = {
client_id = "clientid";
clientSecretFile = "/run/secrets/oidc-client-secret";
response_type = "code";
scope = "openid email name";
discovery_document_uri = "https://auth.example.com/.well-known/openid-configuration";
};
};
};
description = ''
All authentication providers to provision. The attribute name
will only be used to track the provider and does not have any
significance for Firezone.
'';
};
resources = mkOption {
type = types.attrsOf (
types.submodule {
options = {
type = mkOption {
type = types.enum [
"dns"
"cidr"
"ip"
];
description = "The resource type";
};
name = mkOption {
type = types.str;
description = "The name of this resource";
};
address = mkOption {
type = types.str;
description = "The address of this resource. Depending on the resource type, this should be an ip, ip with cidr mask or a domain.";
};
addressDescription = mkOption {
type = types.nullOr types.str;
default = null;
description = "An optional description for resource address, usually a full link to the resource including a schema.";
};
gatewayGroups = mkOption {
type = types.nonEmptyListOf types.str;
description = "A list of gateway groups (sites) which can reach the resource and may be used to connect to it.";
};
filters = mkOption {
type = types.listOf (
types.submodule {
options = {
protocol = mkOption {
type = types.enum [
"icmp"
"tcp"
"udp"
];
description = "The protocol to allow";
};
ports = mkOption {
type = types.listOf typePortRange;
example = [
443
{
from = 8080;
to = 8100;
}
];
default = [ ];
apply =
xs: map (x: if x.from == x.to then toString x.from else "${toString x.from} - ${toString x.to}") xs;
description = "Either a single port or port range to allow. Both bounds are inclusive.";
};
};
}
);
default = [ ];
description = "A list of filter to restrict traffic. If no filters are given, all traffic is allowed.";
};
};
}
);
default = { };
example = {
vaultwarden = {
type = "dns";
name = "Vaultwarden";
address = "vault.example.com";
address_description = "https://vault.example.com";
gatewayGroups = [ "my-site" ];
filters = [
{ protocol = "icmp"; }
{
protocol = "tcp";
ports = [
80
443
];
}
];
};
};
description = ''
All resources to provision. The attribute name will only be used to
track the resource and does not have any significance for Firezone.
'';
};
policies = mkOption {
type = types.attrsOf (
types.submodule {
options = {
description = mkOption {
type = types.nullOr types.str;
description = "The description of this policy";
};
group = mkOption {
type = types.str;
description = "The group which should be allowed access to the given resource.";
};
resource = mkOption {
type = types.str;
description = "The resource to which access should be allowed.";
};
};
}
);
default = { };
example = {
access_vaultwarden = {
name = "Allow anyone to access vaultwarden";
group = "everyone";
resource = "vaultwarden";
};
};
description = ''
All policies to provision. The attribute name will only be used to
track the policy and does not have any significance for Firezone.
'';
};
groups = mkOption {
type = types.attrsOf (
types.submodule {
options = {
name = mkOption {
type = types.str;
description = "The name of this group";
};
members = mkOption {
type = types.listOf types.str;
default = [ ];
description = "The members of this group";
};
forceMembers = mkOption {
type = types.bool;
default = false;
description = "Ensure that only the given members are part of this group at every server start.";
};
};
}
);
default = { };
example = {
users = {
name = "Users";
};
};
description = ''
All groups to provision. The attribute name will only be used
to track the group and does not have any significance for
Firezone.
A group named `everyone` will automatically be managed by Firezone.
'';
};
relayGroups = mkOption {
type = types.attrsOf (
types.submodule {
options = {
name = mkOption {
type = types.str;
description = "The name of this relay group";
};
};
}
);
default = { };
example = {
my-relays = {
name = "My Relays";
};
};
description = ''
All relay groups to provision. The attribute name
will only be used to track the relay group and does not have any
significance for Firezone.
'';
};
gatewayGroups = mkOption {
type = types.attrsOf (
types.submodule {
options = {
name = mkOption {
type = types.str;
description = "The name of this gateway group";
};
};
}
);
default = { };
example = {
my-gateways = {
name = "My Gateways";
};
};
description = ''
All gateway groups (sites) to provision. The attribute name
will only be used to track the gateway group and does not have any
significance for Firezone.
'';
};
};
}
);
default = { };
example = {
main = {
name = "My Account / Organization";
metadata.stripe.billing_email = "org@myorg.example.com";
features.rest_api = false;
};
};
description = ''
All accounts to provision. The attribute name specified here will
become the account slug. By using `"{file:/path/to/file}"` as a
string value anywhere in these settings, the provisioning script will
replace that value with the content of the given file at runtime.
Please refer to the [Firezone source code](https://github.com/firezone/firezone/blob/main/elixir/apps/domain/lib/domain/accounts/account.ex)
for all available properties.
'';
};
};
};
config = mkMerge [
{
assertions = [
{
assertion = cfg.provision.enable -> cfg.domain.enable;
message = "Provisioning must be done on a machine running the firezone domain server";
}
]
++ concatLists (
flip mapAttrsToList cfg.provision.accounts (
accountName: accountCfg:
[
{
assertion = (builtins.match "^[[:lower:]_-]+$" accountName) != null;
message = "An account name must contain only lowercase characters and underscores, as it will be used as the URL slug for this account.";
}
]
++ flip mapAttrsToList accountCfg.auth (
authName: _: {
assertion = (builtins.match "^[[:alnum:]_-]+$" authName) != null;
message = "The authentication provider attribute key must contain only letters, numbers, underscores or dashes.";
}
)
)
);
}
# Enable all components if the main server is enabled
(mkIf cfg.enable {
services.firezone.server.domain.enable = true;
@ -1015,7 +583,7 @@ in
FEATURE_INTERNET_RESOURCE_ENABLED = mkDefault true;
FEATURE_TRAFFIC_FILTERS_ENABLED = mkDefault true;
FEATURE_SIGN_UP_ENABLED = mkDefault (!cfg.provision.enable);
FEATURE_SIGN_UP_ENABLED = mkDefault true;
WEB_EXTERNAL_URL = mkDefault cfg.web.externalUrl;
API_EXTERNAL_URL = mkDefault cfg.api.externalUrl;
@ -1066,19 +634,6 @@ in
OUTBOUND_EMAIL_SMTP_PASSWORD = cfg.smtp.passwordFile;
};
})
(mkIf cfg.provision.enable {
# Load client secrets from authentication providers
services.firezone.server.settingsSecret = flip concatMapAttrs cfg.provision.accounts (
accountName: accountCfg:
flip concatMapAttrs accountCfg.auth (
authName: authCfg:
optionalAttrs (authCfg.adapter_config.clientSecretFile != null) {
"AUTH_CLIENT_SECRET_${toUpper accountName}_${toUpper authName}" =
authCfg.adapter_config.clientSecretFile;
}
)
);
})
(mkIf (cfg.openClusterFirewall && cfg.domain.enable) {
networking.firewall.allowedTCPPorts = [
cfg.domain.settings.ERLANG_DISTRIBUTION_PORT
@ -1156,14 +711,6 @@ in
fi
count=$((count++))
done
''
+ optionalString cfg.provision.enable ''
# Wait for server to fully come up. Not ideal to use sleep, but at least it works.
sleep 1
${loadSecretEnvironment "domain"}
ln -sTf ${provisionStateJson} provision-state.json
${getExe cfg.domain.package} rpc 'Code.eval_file("${./provision.exs}")'
'';
environment = collectEnvironment "domain";

View file

@ -26,6 +26,8 @@ let
toString v
else if lib.isString v then
v
else if lib.isPath v then
v
else if true == v then
"yes"
else if false == v then

View file

@ -128,7 +128,7 @@ let
]
++ lib.optionals cfg.userControlled [
# set up client sockets directory
"+${pkgs.coreutils}/bin/mkdir /run/wpa_supplicant/client"
"+${pkgs.coreutils}/bin/mkdir -p /run/wpa_supplicant/client"
"+${pkgs.coreutils}/bin/chown wpa_supplicant:wpa_supplicant /run/wpa_supplicant/client"
"+${pkgs.coreutils}/bin/chmod g=u /run/wpa_supplicant/client"
];

View file

@ -0,0 +1,95 @@
{
lib,
pkgs,
config,
...
}:
let
cfg = config.services.scx-loader;
settingsFormat = pkgs.formats.toml { };
configFile = settingsFormat.generate "scx_loader.toml" (
lib.filterAttrsRecursive (_: v: v != null) cfg.config
);
schedulers = builtins.concatMap (
x: x.passthru.schedulers or (throw "Scheduler not found in package ${x.name}")
) cfg.schedsPackages;
in
{
options.services.scx-loader = {
enable = lib.mkEnableOption "SCX Loader service, a daemon to run schedulers from userspace using dbus. This requires kernel version 6.12 and later.";
package = lib.mkPackageOption pkgs "scx-loader" { };
schedsPackages = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [ pkgs.scx.rustscheds ];
defaultText = lib.literalExpression "[ pkgs.scx.rustscheds ]";
example = lib.literalExpression "[ pkgs.scx.full ]";
description = ''
`scx` package to use. Defaults to `scx.rustscheds`, which includes all currently by `scx_loader` supported schedulers.
'';
};
config = lib.mkOption {
type = lib.types.submodule {
freeformType = settingsFormat.type;
options = {
default_sched = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "scx_bpfland";
description = ''
Default scheduler that will be started automatically when `scx_loader` starts.
If not set or set to an empty string, `scx_loader` will not start any scheduler by default.
'';
};
};
};
default = { };
description = ''
Configuration for `scx_loader`.
See <https://github.com/sched-ext/scx-loader/blob/main/crates/scx_loader/configuration.md> for the full list of options.
'';
};
};
config = lib.mkIf cfg.enable {
assertions = [
{
assertion = lib.versionAtLeast config.boot.kernelPackages.kernel.version "6.12";
message = "SCX is only supported on kernel version 6.12 and above.";
}
{
assertion = !config.services.scx.enable;
message = "services.scx and services.scx_loader cannot be enabled simultaneously. Please enable only one of them.";
}
{
assertion =
cfg.config.default_sched == null || lib.elem cfg.config.default_sched ([ "" ] ++ schedulers);
message = ''
Invalid default scheduler: ${cfg.config.default_sched}. It must be one of: ${lib.concatStringsSep ", " schedulers}.
'';
}
];
environment.systemPackages = [ cfg.package ] ++ cfg.schedsPackages;
systemd.packages = [ cfg.package ];
services.dbus.packages = [ cfg.package ];
security.polkit.enable = true;
systemd.services.scx_loader = {
path = cfg.schedsPackages;
wantedBy = [ "multi-user.target" ];
serviceConfig = {
TemporaryFileSystem = [ "/etc" ];
BindReadOnlyPaths = [ "${configFile.outPath}:/etc/scx_loader.toml" ];
};
};
};
meta.maintainers = with lib.maintainers; [ ccicnce113424 ];
}

View file

@ -13,6 +13,7 @@ let
file = pkgs.writeText "rule" (builtins.toJSON cfg);
}
);
stateDir = lib.strings.match "/var/lib/([^/]+)/.+" cfg.settings.Rules.Path;
in
{
options = {
@ -105,10 +106,9 @@ in
"iptables"
"nftables"
];
default = if config.networking.nftables.enable then "nftables" else "iptables";
defaultText = lib.literalExpression ''if config.networking.nftables.enable then "nftables" else "iptables"'';
default = "nftables";
description = ''
Which firewall backend to use.
Which firewall backend to use. `nftables` ruleset can be used for `iptables` firewall too, if `iptables` is built with nftables compatibility.
'';
};
Ebpf.ModulesPath = lib.mkOption {
@ -139,7 +139,10 @@ in
};
Rules.Path = lib.mkOption {
type = lib.types.path;
type = lib.types.pathWith {
inStore = false;
absolute = true;
};
default = "/var/lib/opensnitch/rules";
description = ''
Path to the directory where firewall rules can be found and will
@ -158,6 +161,12 @@ in
};
config = lib.mkIf cfg.enable {
assertions = [
{
assertion = stateDir != null;
message = "`config.services.opensnitch.settings.Rules.Path` must be a sub-directory of /var/lib/, currently is ${cfg.settings.Rules.Path}";
}
];
security.auditd = lib.mkIf (cfg.settings.ProcMonitorMethod == "audit") {
enable = true;
@ -174,8 +183,14 @@ in
""
"${lib.getExe' cfg.package "opensnitchd"} --config-file ${cfg.configFile}"
];
StateDirectory = builtins.head stateDir; # match produces a list. Null case covered by assertion.
};
preStart = lib.mkIf (cfg.rules != { }) (
preStart = ''
# assert rules directory exists before service starts
# will be in StateDirectory due to assertion
mkdir -p ${cfg.settings.Rules.Path}
''
+ lib.optionalString (cfg.rules != { }) (
let
rules = lib.flip lib.mapAttrsToList predefinedRules (
file: content: {
@ -205,13 +220,12 @@ in
''
);
};
tmpfiles.rules = [
"d ${cfg.settings.Rules.Path} 0750 root root - -"
"L+ /etc/opensnitchd/network_aliases.json - - - - ${cfg.package}/etc/opensnitchd/network_aliases.json"
"L+ /etc/opensnitchd/system-fw.json - - - - ${cfg.package}/etc/opensnitchd/system-fw.json"
];
};
environment.etc."opensnitchd/network_aliases.json".source =
"${cfg.package}/etc/opensnitchd/network_aliases.json";
environment.etc."opensnitchd/system-fw.json".source =
"${cfg.package}/etc/opensnitchd/system-fw.json";
};
meta.maintainers = with lib.maintainers; [

View file

@ -72,7 +72,7 @@ in
ExecStart = lib.getExe pkgs.lauti;
WorkingDirectory = cfg.dataDir;
StateDirectory = default;
EnvironmentFile = [ cfg.secrets ];
EnvironmentFile = cfg.secrets;
# hardening
AmbientCapabilities = "";

View file

@ -71,10 +71,12 @@ in
};
};
system.activationScripts.setup-cde = ''
mkdir -p /var/dt/{tmp,appconfig/appmanager}
chmod a+w+t /var/dt/{tmp,appconfig/appmanager}
'';
systemd.tmpfiles.settings."10-cde" = {
"/var/dt".d.mode = "0755";
"/var/dt/tmp".d.mode = "1777";
"/var/dt/appconfig".d.mode = "0755";
"/var/dt/appconfig/appmanager".d.mode = "1777";
};
services.xserver.desktopManager.session = [
{

View file

@ -97,13 +97,13 @@ let
};
in
{
imports = [
(lib.mkRemovedOptionModule [ "boot" "bootspec" "enable" ] ''
Bootspec is now always generated and can no longer be disabled.
'')
];
options.boot.bootspec = {
enable =
lib.mkEnableOption "the generation of RFC-0125 bootspec in $system/boot.json, e.g. /run/current-system/boot.json"
// {
default = true;
internal = true;
};
enableValidation = lib.mkEnableOption ''
the validation of bootspec documents for each build.
This will introduce Go in the build-time closure as we are relying on [Cuelang](https://cuelang.org/) for schema validation.

View file

@ -44,7 +44,7 @@ let
printf "%s " "''${extraDependencies[@]}" > "$out/extra-dependencies"
${optionalString (!config.boot.isContainer && config.boot.bootspec.enable) ''
${optionalString (!config.boot.isContainer) ''
${config.boot.bootspec.writer}
${optionalString config.boot.bootspec.enableValidation ''${config.boot.bootspec.validator} "$out/${config.boot.bootspec.filename}"''}
''}
@ -139,8 +139,8 @@ in
system.boot.loader.kernelFile = mkOption {
internal = true;
default = pkgs.stdenv.hostPlatform.linux-kernel.target;
defaultText = literalExpression "pkgs.stdenv.hostPlatform.linux-kernel.target";
default = config.boot.kernelPackages.kernel.target;
defaultText = literalExpression "config.boot.kernelPackages.kernel.target";
type = types.str;
description = ''
Name of the kernel file to be passed to the bootloader.

View file

@ -66,7 +66,6 @@ in
system.build.installBootLoader = generationsDirBuilder;
system.boot.loader.id = "generationsDir";
system.boot.loader.kernelFile = pkgs.stdenv.hostPlatform.linux-kernel.target;
};
}

View file

@ -503,7 +503,9 @@ in
script = ''
fwupd_efi=(${config.services.fwupd.package.fwupd-efi}/libexec/fwupd/efi/fwupd*.efi)
${lib.getExe cfg.secureBoot.sbctl} sign -o /run/fwupd-efi/$(basename "$fwupd_efi").signed "$fwupd_efi"
for efi in "''${fwupd_efi[@]}"; do
${lib.getExe cfg.secureBoot.sbctl} sign -o "/run/fwupd-efi/$(basename "$efi").signed" "$efi"
done
'';
};

View file

@ -27,7 +27,6 @@ NIXOS_DIR = Path(
TIMEOUT = "@timeout@"
EDITOR = "@editor@" == "1" # noqa: PLR0133
CONSOLE_MODE = "@consoleMode@"
BOOTSPEC_TOOLS = "@bootspecTools@"
DISTRO_NAME = "@distroName@"
NIX = "@nix@"
SYSTEMD = "@systemd@"
@ -291,31 +290,26 @@ def write_loader_conf(default_entry_id: str | None) -> None:
os.rename(tmp, LOADER_CONF)
def get_bootspec(profile: str | None, generation: int) -> BootSpec:
def get_bootspec(profile: str | None, generation: int) -> BootSpec | None:
system_directory = system_dir(profile, generation, None)
boot_json_path = (system_directory / "boot.json").resolve()
if boot_json_path.is_file():
with boot_json_path.open("r") as f:
# check if json is well-formed, else throw error with filepath
try:
bootspec_json = json.load(f)
except ValueError as e:
print(
f"error: Malformed Json: {e}, in {boot_json_path}", file=sys.stderr
)
sys.exit(1)
else:
boot_json_str = run(
[
f"{BOOTSPEC_TOOLS}/bin/synthesize",
"--version",
"1",
system_directory,
"/dev/stdout",
],
stdout=subprocess.PIPE,
).stdout
bootspec_json = json.loads(boot_json_str)
if not boot_json_path.is_file():
print(
f"warning: skipping generation {generation}"
+ (f" of profile {profile}" if profile else "")
+ f": {boot_json_path} does not exist",
file=sys.stderr,
)
return None
with boot_json_path.open("r") as f:
# check if json is well-formed, else throw error with filepath
try:
bootspec_json = json.load(f)
except ValueError as e:
print(
f"error: Malformed Json: {e}, in {boot_json_path}", file=sys.stderr
)
sys.exit(1)
return bootspec_from_json(bootspec_json)
@ -551,6 +545,8 @@ def install_bootloader(args: argparse.Namespace) -> None:
for gen in gens:
bootspec = get_bootspec(gen.profile, gen.generation)
if bootspec is None:
continue
is_default = Path(bootspec.init).parent == default_config
new_boot_files, new_bootctl_id = boot_file(*gen, machine_id, bootspec)
boot_files.extend(new_boot_files)

View file

@ -47,8 +47,6 @@ let
systemd = config.systemd.package;
bootspecTools = config.boot.bootspec.package;
nix = config.nix.package.out;
timeout = if config.boot.loader.timeout == null then "menu-force" else config.boot.loader.timeout;

View file

@ -40,6 +40,7 @@ let
"network-online.target"
"nss-lookup.target"
"nss-user-lookup.target"
"time-set.target"
"time-sync.target"
"first-boot-complete.target"
]
@ -808,7 +809,7 @@ in
systemd.targets.remote-fs.unitConfig.X-StopOnReconfiguration = true;
systemd.services.systemd-importd = lib.mkIf cfg.package.withImportd {
environment = proxy_env;
path = [ pkgs.gnupg ];
path = [ pkgs.gnupgMinimal ];
};
systemd.services.systemd-pstore.wantedBy = [ "sysinit.target" ]; # see #81138

View file

@ -64,13 +64,13 @@
environment.etc."systemd/logind.conf".text =
utils.systemdUtils.lib.settingsToSections config.services.logind.settings;
# Restarting systemd-logind breaks X11
# Restarting systemd-logind breaks X11 and other user sessions.
# However, reloading the service seems to do the trick of loading new configuration without breaking anything.
# - upstream commit: https://cgit.freedesktop.org/xorg/xserver/commit/?id=dc48bd653c7e101
# - systemd announcement: https://github.com/systemd/systemd/blob/22043e4317ecd2bc7834b48a6d364de76bb26d91/NEWS#L103-L112
# - this might be addressed in the future by xorg
#systemd.services.systemd-logind.restartTriggers = [ config.environment.etc."systemd/logind.conf".source ];
systemd.services.systemd-logind.restartIfChanged = false;
systemd.services.systemd-logind.stopIfChanged = false;
systemd.services.systemd-logind.reloadIfChanged = true;
# The user-runtime-dir@ service is managed by systemd-logind we should not touch it or else we break the users' sessions.
systemd.services."user-runtime-dir@".stopIfChanged = false;

View file

@ -138,7 +138,7 @@ in
systemd.services.systemd-sysupdated = {
aliases = [ "dbus-org.freedesktop.sysupdate1.service" ];
path = [ pkgs.gnupg ];
path = [ pkgs.gnupgMinimal ];
};
systemd.timers = {

View file

@ -5,16 +5,13 @@
utils,
...
}:
with utils;
with systemdUtils.unitOptions;
with lib;
let
cfg = config.systemd.user;
systemd = config.systemd.package;
inherit (systemdUtils.lib)
inherit (utils.systemdUtils.lib)
generateUnits
targetToUnit
serviceToUnit
@ -53,7 +50,7 @@ let
user ? null,
}:
let
suffix = optionalString (user != null) "-${user}";
suffix = lib.optionalString (user != null) "-${user}";
in
pkgs.writeTextFile {
name = "nixos-user-tmpfiles.d${suffix}";
@ -61,74 +58,86 @@ let
text = ''
# This file is created automatically and should not be modified.
# Please change the options systemd.user.tmpfiles instead.
${concatStringsSep "\n" rules}
${lib.concatStringsSep "\n" rules}
'';
};
in
{
imports = [
(lib.mkRemovedOptionModule [
"systemd"
"user"
"extraConfig"
] "Use systemd.user.settings.Manager instead.")
];
options = {
systemd.user.extraConfig = mkOption {
default = "";
type = types.lines;
example = "DefaultTimeoutStartSec=60";
systemd.user.settings.Manager = lib.mkOption {
default = { };
type = lib.types.submodule {
freeformType = lib.types.attrsOf utils.systemdUtils.unitOptions.unitOption;
};
example = {
DefaultTimeoutStartSec = 60;
};
description = ''
Extra config options for systemd user instances. See {manpage}`systemd-user.conf(5)` for
available options.
Settings for systemd user instances. See {manpage}`systemd-user.conf(5)`
for available options.
'';
};
systemd.user.units = mkOption {
systemd.user.units = lib.mkOption {
description = "Definition of systemd per-user units.";
default = { };
type = systemdUtils.types.units;
type = utils.systemdUtils.types.units;
};
systemd.user.paths = mkOption {
systemd.user.paths = lib.mkOption {
default = { };
type = systemdUtils.types.paths;
type = utils.systemdUtils.types.paths;
description = "Definition of systemd per-user path units.";
};
systemd.user.services = mkOption {
systemd.user.services = lib.mkOption {
default = { };
type = systemdUtils.types.services;
type = utils.systemdUtils.types.services;
description = "Definition of systemd per-user service units.";
};
systemd.user.slices = mkOption {
systemd.user.slices = lib.mkOption {
default = { };
type = systemdUtils.types.slices;
type = utils.systemdUtils.types.slices;
description = "Definition of systemd per-user slice units.";
};
systemd.user.sockets = mkOption {
systemd.user.sockets = lib.mkOption {
default = { };
type = systemdUtils.types.sockets;
type = utils.systemdUtils.types.sockets;
description = "Definition of systemd per-user socket units.";
};
systemd.user.targets = mkOption {
systemd.user.targets = lib.mkOption {
default = { };
type = systemdUtils.types.targets;
type = utils.systemdUtils.types.targets;
description = "Definition of systemd per-user target units.";
};
systemd.user.timers = mkOption {
systemd.user.timers = lib.mkOption {
default = { };
type = systemdUtils.types.timers;
type = utils.systemdUtils.types.timers;
description = "Definition of systemd per-user timer units.";
};
systemd.user.tmpfiles = {
enable =
(mkEnableOption "systemd user units systemd-tmpfiles-setup.service and systemd-tmpfiles-clean.timer")
(lib.mkEnableOption "systemd user units systemd-tmpfiles-setup.service and systemd-tmpfiles-clean.timer")
// {
default = true;
example = false;
};
rules = mkOption {
type = types.listOf types.str;
rules = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
example = [ "D %C - - - 7d" ];
description = ''
@ -139,17 +148,17 @@ in
'';
};
users = mkOption {
users = lib.mkOption {
description = ''
Per-user rules for creation, deletion and cleaning of volatile and
temporary files automatically.
'';
default = { };
type = types.attrsOf (
types.submodule {
type = lib.types.attrsOf (
lib.types.submodule {
options = {
rules = mkOption {
type = types.listOf types.str;
rules = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
example = [ "D %C - - - 7d" ];
description = ''
@ -165,8 +174,8 @@ in
};
};
systemd.user.generators = mkOption {
type = types.attrsOf types.path;
systemd.user.generators = lib.mkOption {
type = lib.types.attrsOf lib.types.path;
default = { };
example = {
systemd-gpt-auto-generator = "/dev/null";
@ -179,9 +188,9 @@ in
'';
};
systemd.additionalUpstreamUserUnits = mkOption {
systemd.additionalUpstreamUserUnits = lib.mkOption {
default = [ ];
type = types.listOf types.str;
type = lib.types.listOf lib.types.str;
example = [ ];
description = ''
Additional units shipped with systemd that should be enabled for per-user systemd instances.
@ -203,29 +212,26 @@ in
upstreamWants = [ ];
};
"systemd/user.conf".text = ''
[Manager]
${cfg.extraConfig}
'';
"systemd/user.conf".text = utils.systemdUtils.lib.settingsToSections cfg.settings;
};
systemd.user.units =
mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit v)) cfg.paths
// mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit v)) cfg.services
// mapAttrs' (n: v: nameValuePair "${n}.slice" (sliceToUnit v)) cfg.slices
// mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit v)) cfg.sockets
// mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit v)) cfg.targets
// mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit v)) cfg.timers;
lib.mapAttrs' (n: v: lib.nameValuePair "${n}.path" (pathToUnit v)) cfg.paths
// lib.mapAttrs' (n: v: lib.nameValuePair "${n}.service" (serviceToUnit v)) cfg.services
// lib.mapAttrs' (n: v: lib.nameValuePair "${n}.slice" (sliceToUnit v)) cfg.slices
// lib.mapAttrs' (n: v: lib.nameValuePair "${n}.socket" (socketToUnit v)) cfg.sockets
// lib.mapAttrs' (n: v: lib.nameValuePair "${n}.target" (targetToUnit v)) cfg.targets
// lib.mapAttrs' (n: v: lib.nameValuePair "${n}.timer" (timerToUnit v)) cfg.timers;
systemd.user.timers = {
# enable systemd user tmpfiles
systemd-tmpfiles-clean.wantedBy = optional cfg.tmpfiles.enable "timers.target";
systemd-tmpfiles-clean.wantedBy = lib.optional cfg.tmpfiles.enable "timers.target";
}
# Generate timer units for all services that have a startAt value.
// (mapAttrs (name: service: {
// (lib.mapAttrs (name: service: {
wantedBy = [ "timers.target" ];
timerConfig.OnCalendar = service.startAt;
}) (filterAttrs (name: service: service.startAt != [ ]) cfg.services));
}) (lib.filterAttrs (name: service: service.startAt != [ ]) cfg.services));
# Provide the systemd-user PAM service, required to run systemd
# user instances.
@ -244,18 +250,19 @@ in
systemd.services.systemd-user-sessions.restartIfChanged = false; # Restart kills all active sessions.
# enable systemd user tmpfiles
systemd.user.services.systemd-tmpfiles-setup.wantedBy = optional cfg.tmpfiles.enable "basic.target";
systemd.user.services.systemd-tmpfiles-setup.wantedBy =
lib.optional cfg.tmpfiles.enable "basic.target";
# /run/current-system/sw/etc/xdg is in systemd's $XDG_CONFIG_DIRS so we can
# write the tmpfiles.d rules for everyone there
environment.systemPackages = optional (cfg.tmpfiles.rules != [ ]) (writeTmpfiles {
environment.systemPackages = lib.optional (cfg.tmpfiles.rules != [ ]) (writeTmpfiles {
inherit (cfg.tmpfiles) rules;
});
# /etc/profiles/per-user/$USER/etc/xdg is in systemd's $XDG_CONFIG_DIRS so
# we can write a single user's tmpfiles.d rules there
users.users = mapAttrs (user: cfg': {
packages = optional (cfg'.rules != [ ]) (writeTmpfiles {
users.users = lib.mapAttrs (user: cfg': {
packages = lib.optional (cfg'.rules != [ ]) (writeTmpfiles {
inherit (cfg') rules;
inherit user;
});

View file

@ -238,11 +238,11 @@ in
'';
systemd.settings.Manager = managerSettings;
systemd.user.extraConfig = ''
systemd.user.settings.Manager = {
# Allow very slow start
DefaultTimeoutStartSec=300
DefaultDeviceTimeoutSec=300
'';
DefaultTimeoutStartSec = 300;
DefaultDeviceTimeoutSec = 300;
};
boot.consoleLogLevel = 7;

View file

@ -1307,6 +1307,8 @@ in
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
User = lib.mkIf (config.nix.daemonUser != "root") config.nix.daemonUser;
Group = lib.mkIf (config.nix.daemonGroup != "root") config.nix.daemonGroup;
};
script = ''
if [[ "$(cat /proc/cmdline)" =~ regInfo=([^ ]*) ]]; then
@ -1379,7 +1381,6 @@ in
"-device usb-tablet,bus=usb-bus.0"
])
(mkIf pkgs.stdenv.hostPlatform.isAarch [
"-device virtio-gpu-pci"
"-device usb-ehci,id=usb0"
"-device usb-kbd"
"-device usb-tablet"

View file

@ -152,7 +152,7 @@ let
tarball
// {
meta = {
description = "NixOS system tarball for ${system} - ${stdenv.hostPlatform.linux-kernel.name}";
description = "NixOS system tarball for ${system}";
maintainers = map (x: lib.maintainers.${x}) maintainers;
};
inherit config;
@ -188,7 +188,7 @@ let
modules = makeModules module { };
};
build = configEvaled.config.system.build;
kernelTarget = configEvaled.pkgs.stdenv.hostPlatform.linux-kernel.target;
kernelTarget = build.kernel.target;
in
configEvaled.pkgs.symlinkJoin {
name = "netboot";

View file

@ -695,6 +695,7 @@ in
grocy = runTest ./grocy.nix;
grow-partition = runTest ./grow-partition.nix;
grub = runTest ./grub.nix;
gs1200-exporter = runTest ./gs1200-exporter.nix;
guacamole-server = runTest ./guacamole-server.nix;
guix = handleTest ./guix { };
gvisor = runTest ./gvisor.nix;
@ -1489,11 +1490,13 @@ in
sane = runTest ./sane.nix;
sanoid = runTest ./sanoid.nix;
saunafs = runTest ./saunafs.nix;
scanservjs = runTest ./scanservjs.nix;
scaphandre = runTest ./scaphandre.nix;
schleuder = runTest ./schleuder.nix;
scion-freestanding-deployment = runTest ./scion/freestanding-deployment;
scrutiny = runTest ./scrutiny.nix;
scx = runTest ./scx/default.nix;
scx-loader = runTest ./scx/loader.nix;
sddm = import ./sddm.nix { inherit runTest; };
sdl3 = runTest ./sdl3.nix;
searx = runTest ./searx.nix;
@ -1670,6 +1673,7 @@ in
systemd-timesyncd-nscd-dnssec = runTest ./systemd-timesyncd-nscd-dnssec.nix;
systemd-user-linger = runTest ./systemd-user-linger.nix;
systemd-user-linger-purge = runTest ./systemd-user-linger-purge.nix;
systemd-user-settings = runTest ./systemd-user-settings.nix;
systemd-user-tmpfiles-rules = runTest ./systemd-user-tmpfiles-rules.nix;
systemd-userdbd = runTest ./systemd-userdbd.nix;
systemtap = handleTest ./systemtap.nix { };

View file

@ -24,8 +24,6 @@ let
environment.systemPackages = [ pkgs.efibootmgr ];
};
standard = {
boot.bootspec.enable = true;
imports = [
baseline
systemd-boot
@ -53,8 +51,6 @@ in
meta.maintainers = with pkgs.lib.maintainers; [ raitobezarius ];
nodes.machine = {
boot.bootspec.enable = true;
imports = [
baseline
grub
@ -75,8 +71,6 @@ in
meta.maintainers = with pkgs.lib.maintainers; [ raitobezarius ];
nodes.machine = {
boot.bootspec.enable = true;
imports = [
baseline
grub

View file

@ -1,86 +0,0 @@
alias Domain.{Repo, Accounts, Auth, Actors, Tokens}
mappings = case File.read("provision-uuids.json") do
{:ok, content} ->
case Jason.decode(content) do
{:ok, mapping} -> mapping
_ -> %{"accounts" => %{}}
end
_ -> %{"accounts" => %{}}
end
IO.puts("INFO: Fetching account")
{:ok, account} = Accounts.fetch_account_by_id_or_slug("main")
IO.puts("INFO: Fetching email provider")
{:ok, email_provider} = Auth.Provider.Query.not_disabled()
|> Auth.Provider.Query.by_adapter(:email)
|> Auth.Provider.Query.by_account_id(account.id)
|> Repo.fetch(Auth.Provider.Query, [])
temp_admin_actor_email = "firezone-provision@localhost.local"
temp_admin_actor_context = %Auth.Context{
type: :browser,
user_agent: "Unspecified/0.0",
remote_ip: {127, 0, 0, 1},
remote_ip_location_region: "N/A",
remote_ip_location_city: "N/A",
remote_ip_location_lat: 0.0,
remote_ip_location_lon: 0.0
}
{:ok, temp_admin_actor} =
Actors.create_actor(account, %{
type: :account_admin_user,
name: "Token Provisioning"
})
{:ok, temp_admin_actor_email_identity} =
Auth.create_identity(temp_admin_actor, email_provider, %{
provider_identifier: temp_admin_actor_email,
provider_identifier_confirmation: temp_admin_actor_email
})
{:ok, temp_admin_actor_token} =
Auth.create_token(temp_admin_actor_email_identity, temp_admin_actor_context, "temporarynonce", DateTime.utc_now() |> DateTime.add(1, :hour))
{:ok, temp_admin_subject} =
Auth.build_subject(temp_admin_actor_token, temp_admin_actor_context)
{:ok, relay_group_token} =
Tokens.create_token(%{
"type" => :relay_group,
"expires_at" => DateTime.utc_now() |> DateTime.add(1, :hour),
"secret_fragment" => Domain.Crypto.random_token(32, encoder: :hex32),
"relay_group_id" => get_in(mappings, ["accounts", "main", "relay_groups", "my-relays"])
})
relay_group_encoded_token = Tokens.encode_fragment!(relay_group_token)
IO.puts("Created relay token: #{relay_group_encoded_token}")
File.write("relay_token.txt", relay_group_encoded_token)
{:ok, gateway_group_token} =
Tokens.create_token(%{
"type" => :gateway_group,
"expires_at" => DateTime.utc_now() |> DateTime.add(1, :hour),
"secret_fragment" => Domain.Crypto.random_token(32, encoder: :hex32),
"account_id" => get_in(mappings, ["accounts", "main", "id"]),
"gateway_group_id" => get_in(mappings, ["accounts", "main", "gateway_groups", "site"])
}, temp_admin_subject)
gateway_group_encoded_token = Tokens.encode_fragment!(gateway_group_token)
IO.puts("Created gateway group token: #{gateway_group_encoded_token}")
File.write("gateway_token.txt", gateway_group_encoded_token)
{:ok, service_account_actor_token} =
Tokens.create_token(%{
"type" => :client,
"expires_at" => DateTime.utc_now() |> DateTime.add(1, :hour),
"secret_fragment" => Domain.Crypto.random_token(32, encoder: :hex32),
"account_id" => get_in(mappings, ["accounts", "main", "id"]),
"actor_id" => get_in(mappings, ["accounts", "main", "actors", "client"])
})
service_account_actor_encoded_token = Tokens.encode_fragment!(service_account_actor_token)
IO.puts("Created service actor token: #{service_account_actor_encoded_token}")
File.write("client_token.txt", service_account_actor_encoded_token)

View file

@ -84,89 +84,15 @@ in
passwordFile = pkgs.writeText "tmpmailpasswd" "verysecurepassword";
};
provision = {
enable = true;
accounts.main = {
name = "My Account";
relayGroups.my-relays.name = "Relays";
gatewayGroups.site.name = "Site";
actors = {
admin = {
type = "account_admin_user";
name = "Admin";
email = "admin@localhost.localdomain";
};
client = {
type = "service_account";
name = "A client";
email = "client@example.com";
};
};
# service accounts aren't members of 'Everyone' so we need to add a separate group
groups.main = {
name = "main";
members = [
"client"
"admin"
];
};
resources.res1 = {
type = "dns";
name = "Dns Resource";
address = "resource.example.com";
gatewayGroups = [ "site" ];
filters = [
{ protocol = "icmp"; }
{
protocol = "tcp";
ports = [ 80 ];
}
];
};
resources.res2 = {
type = "ip";
name = "Ip Resource";
address = "172.20.2.1";
gatewayGroups = [ "site" ];
};
resources.res3 = {
type = "cidr";
name = "Cidr Resource";
address = "172.20.1.0/24";
gatewayGroups = [ "site" ];
};
policies.pol1 = {
description = "Allow anyone res1 access";
group = "main";
resource = "res1";
};
policies.pol2 = {
description = "Allow anyone res2 access";
group = "main";
resource = "res2";
};
policies.pol3 = {
description = "Allow anyone res3 access";
group = "main";
resource = "res3";
};
};
};
api.externalUrl = "https://${domain}/api/";
web.externalUrl = "https://${domain}/";
};
systemd.services.firezone-server-domain.postStart = lib.mkAfter ''
${lib.getExe config.services.firezone.server.domain.package} rpc 'Code.eval_file("${./create-tokens.exs}")'
'';
};
relay =
{
nodes,
config,
lib,
...
}:
{
@ -181,14 +107,11 @@ in
logLevel = "debug";
name = "test-relay";
apiUrl = "wss://${domain}/api/";
tokenFile = "/tmp/shared/relay_token.txt";
tokenFile = pkgs.writeText "token" "token";
publicIpv4 = config.networking.primaryIPAddress;
publicIpv6 = config.networking.primaryIPv6Address;
openFirewall = true;
};
# Don't auto-start so we can wait until the token was provisioned
systemd.services.firezone-relay.wantedBy = lib.mkForce [ ];
};
# A resource that is only connected to the gateway,
@ -300,11 +223,8 @@ in
logLevel = "debug";
name = "test-gateway";
apiUrl = "wss://${domain}/api/";
tokenFile = "/tmp/shared/gateway_token.txt";
tokenFile = pkgs.writeText "token" "token";
};
# Don't auto-start so we can wait until the token was provisioned
systemd.services.firezone-gateway.wantedBy = lib.mkForce [ ];
};
client =
@ -326,11 +246,8 @@ in
logLevel = "debug";
name = "test-client-somebody";
apiUrl = "wss://${domain}/api/";
tokenFile = "/tmp/shared/client_token.txt";
tokenFile = pkgs.writeText "token" "token";
};
# Don't auto-start so we can wait until the token was provisioned
systemd.services.firezone-headless-client.wantedBy = lib.mkForce [ ];
};
};
@ -344,43 +261,13 @@ in
server.wait_until_succeeds("curl -Lsf https://${domain} | grep 'Welcome to Firezone'")
server.wait_until_succeeds("curl -Ls https://${domain}/api | grep 'Not Found'")
# Wait for tokens and copy them to shared folder
server.wait_for_file("/var/lib/private/firezone/relay_token.txt")
server.wait_for_file("/var/lib/private/firezone/gateway_token.txt")
server.wait_for_file("/var/lib/private/firezone/client_token.txt")
server.succeed("cp /var/lib/private/firezone/*_token.txt /tmp/shared")
with subtest("Connect relay"):
relay.succeed("systemctl start firezone-relay")
relay.wait_for_unit("firezone-relay.service")
relay.wait_until_succeeds("journalctl --since -2m --unit firezone-relay.service --grep 'Connected to portal.*${domain}'", timeout=30)
with subtest("Connect gateway"):
gateway.succeed("systemctl start firezone-gateway")
gateway.wait_for_unit("firezone-gateway.service")
gateway.wait_until_succeeds("journalctl --since -2m --unit firezone-gateway.service --grep 'Connected to portal.*${domain}'", timeout=30)
relay.wait_until_succeeds("journalctl --since -2m --unit firezone-relay.service --grep 'Created allocation.*IPv4'", timeout=30)
relay.wait_until_succeeds("journalctl --since -2m --unit firezone-relay.service --grep 'Created allocation.*IPv6'", timeout=30)
# Assert both relay ips are known
gateway.wait_until_succeeds("journalctl --since -2m --unit firezone-gateway.service --grep 'Updated allocation.*relay_ip4.*Some.*relay_ip6.*Some'", timeout=30)
with subtest("Connect headless-client"):
client.succeed("systemctl start firezone-headless-client")
client.wait_for_unit("firezone-headless-client.service")
client.wait_until_succeeds("journalctl --since -2m --unit firezone-headless-client.service --grep 'Connected to portal.*${domain}'", timeout=30)
client.wait_until_succeeds("journalctl --since -2m --unit firezone-headless-client.service --grep 'Tunnel ready'", timeout=30)
with subtest("Check DNS based access"):
# Check that we can access the resource through the VPN via DNS
client.wait_until_succeeds("curl -4 -Lsf http://resource.example.com | grep 'greetings from the resource'")
with subtest("Check CIDR based access"):
# Check that we can access the resource through the VPN via CIDR
client.wait_until_succeeds("ping -c1 -W1 172.20.1.1")
with subtest("Check IP based access"):
# Check that we can access the resource through the VPN via IP
client.wait_until_succeeds("ping -c1 -W1 172.20.2.1")
'';
}

View file

@ -0,0 +1,20 @@
{ lib, ... }:
{
name = "gs1200-exporter";
meta.maintainers = with lib.maintainers; [ DerGrumpf ];
nodes.machine = _: {
services.gs1200-exporter = {
enable = true;
address = "192.168.2.4";
passwordFile = "/run/secrets/gs1200-password";
};
systemd.tmpfiles.rules = [
"f /run/secrets/gs1200-password 0400 root root - testpassword"
];
};
testScript = ''
machine.wait_for_unit("gs1200-exporter.service")
machine.wait_for_open_port(9934)
machine.succeed("curl -f http://localhost:9934/metrics")
'';
}

View file

@ -793,7 +793,6 @@ let
++ optionals (bootLoader == "systemd-boot") [
pkgs.zstd.bin
pkgs.mypy
config.boot.bootspec.package
]
++ optionals clevisTest [ pkgs.klibc ]
++ optional systemdStage1 config.system.nixos-init.package;

View file

@ -1,17 +1,23 @@
{ pkgs, lib, ... }:
let
# opensnitch ebpf seems to handle non-x86 syscalls incorrectly
test_ebpf = pkgs.stdenv.hostPlatform.isx86;
monitorMethods = [
"ebpf"
"proc"
"ftrace"
"audit"
];
]
++ lib.optional test_ebpf "ebpf";
in
{
name = "opensnitch";
meta = with pkgs.lib.maintainers; {
maintainers = [ onny ];
maintainers = [
onny
grimmauld
];
};
nodes = {
@ -88,13 +94,13 @@ in
lib.concatLines (
map (m: ''
client_blocked_${m}.wait_for_unit("opensnitchd.service")
client_blocked_${m}.fail("curl http://server")
client_blocked_${m}.fail("curl --connect-timeout 3 http://server")
client_allowed_${m}.wait_for_unit("opensnitchd.service")
client_allowed_${m}.succeed("curl http://server")
client_allowed_${m}.succeed("curl --connect-timeout 3 http://server")
'') monitorMethods
)
+ ''
+ lib.optionalString test_ebpf ''
# make sure the kernel modules were actually properly loaded
client_blocked_ebpf.succeed(r"journalctl -u opensnitchd --grep '\[eBPF\] module loaded: /nix/store/.*/etc/opensnitchd/opensnitch\.o'")
client_blocked_ebpf.succeed(r"journalctl -u opensnitchd --grep '\[eBPF\] module loaded: /nix/store/.*/etc/opensnitchd/opensnitch-procs\.o'")

View file

@ -0,0 +1,23 @@
let
port = 1234;
in
{
name = "scanservjs";
nodes.machine =
{ ... }:
{
services.scanservjs = {
enable = true;
settings.host = "0.0.0.0";
settings.port = port;
};
};
testScript = ''
machine.wait_for_unit("scanservjs.service")
machine.wait_until_succeeds(
"curl --silent --fail --show-error --location http://localhost:${toString port}"
)
'';
}

View file

@ -0,0 +1,20 @@
{ pkgs, lib, ... }:
{
name = "scx-loader";
meta.maintainers = with lib.maintainers; [ ccicnce113424 ];
nodes.machine = {
boot.kernelPackages = pkgs.linuxPackages_latest;
services.scx-loader = {
enable = true;
config.default_sched = "scx_bpfland";
};
};
testScript = ''
machine.wait_for_unit("multi-user.target")
output = machine.succeed("scxctl get").strip()
assert output == "running Bpfland in Auto mode", f"Unexpected scxctl output: {output!r}"
'';
}

View file

@ -88,6 +88,9 @@
machine.succeed("shadps4 /etc/openorbis-sample-packages/OpenOrbis-PNG-Sample/uroot/eboot.bin >&2 &")
machine.wait_for_text("Save Migration")
machine.succeed("xdotool mousemove 879 182 click 1")
# Look for logo
with machine.nested("Waiting for the screen to have openorbisColor {} on it:".format(openorbisColor)):
retry(check_for_color(openorbisColor))

View file

@ -79,7 +79,6 @@ in
meta = {
maintainers = with lib.maintainers; [
happysalada
euxane
onny
];
};

View file

@ -772,26 +772,6 @@ in
}
);
no-bootspec = runTest (
{ lib, ... }:
{
name = "systemd-boot-no-bootspec";
meta.maintainers = with lib.maintainers; [ julienmalka ];
nodes.machine = {
imports = [ common ];
boot.bootspec.enable = false;
};
testScript =
# python
''
machine.start()
machine.wait_for_unit("multi-user.target")
'';
}
);
bootCounting =
let
baseConfig = {

View file

@ -0,0 +1,24 @@
{
name = "systemd-user-settings";
meta = {
maintainers = [ ];
};
nodes.machine =
{ lib, ... }:
{
systemd.user.settings.Manager = {
DefaultTimeoutStartSec = lib.mkForce "60";
DefaultEnvironment = "FOO=bar";
};
};
testScript = ''
machine.wait_for_unit("multi-user.target")
with subtest("settings.Manager renders user.conf"):
machine.succeed("grep -F '[Manager]' /etc/systemd/user.conf")
machine.succeed("grep -F 'DefaultTimeoutStartSec=60' /etc/systemd/user.conf")
machine.succeed("grep -F 'DefaultEnvironment=FOO=bar' /etc/systemd/user.conf")
'';
}

View file

@ -34,7 +34,7 @@
RebootWatchdogSec = "10min";
KExecWatchdogSec = "5min";
};
systemd.user.extraConfig = "DefaultEnvironment=\"XXX_USER=bar\"";
systemd.user.settings.Manager.DefaultEnvironment = "\"XXX_USER=bar\"";
services.journald.extraConfig = "Storage=volatile";
test-support.displayManager.auto.user = "alice";

View file

@ -283,18 +283,22 @@ in
enable = lib.mkOverride 0 true;
userControlled = true;
allowAuxiliaryImperativeNetworks = true;
interfaces = [ "wlan1" ];
interfaces = [
"wlan0"
"wlan1"
];
};
};
testScript = ''
wpa_cli = "sudo -u alice -g wpa_supplicant wpa_cli"
with subtest("Daemon is running and accepting connections"):
machine.wait_for_unit("wpa_supplicant-wlan1.service")
status = machine.wait_until_succeeds(f"{wpa_cli} -i wlan1 status")
assert "Failed to connect" not in status, \
"Failed to connect to the daemon"
with subtest("Daemons are running and accepting connections"):
for iface in ["wlan0", "wlan1"]:
machine.wait_for_unit(f"wpa_supplicant-{iface}.service")
status = machine.wait_until_succeeds(f"{wpa_cli} -i {iface} status")
assert "Failed to connect" not in status, \
f"Failed to connect to the daemon for {iface}"
with subtest("Daemon can be configured imperatively"):
machine.succeed(f"{wpa_cli} -i wlan1 add_network")

View file

@ -84,7 +84,7 @@
client.wait_for_unit("multi-user.target")
for be in ["fs", "rocksdb", "mem" ]:
client.succeed(f"mosquitto_pub -h router -t {be}/test -m hello")
client.succeed(f"mosquitto_pub -q 1 -h router -t {be}/test -m hello")
client.succeed(f"curl router:8000/{be}/test | grep hello")
'';
}

View file

@ -16,9 +16,9 @@ let
inherit tiling_wm;
};
stableVersion = {
version = "2025.3.4.7"; # "Android Studio Panda 4 | 2025.3.4 Patch 1"
sha256Hash = "sha256-qujzMvEkr9I8pJXcdwkVpFbadIDI+FngFTWtQvy0ygY=";
url = "https://edgedl.me.gvt1.com/android/studio/ide-zips/2025.3.4.7/android-studio-panda4-patch1-linux.tar.gz";
version = "2026.1.1.8"; # "Android Studio Quail 1 | 2026.1.1"
sha256Hash = "sha256-DB+kujz6vQfkipDgCl+i6iqCzVhwgz2tpbApDIF9g9M=";
url = "https://edgedl.me.gvt1.com/android/studio/ide-zips/2026.1.1.8/android-studio-quail1-linux.tar.gz";
};
betaVersion = {
version = "2026.1.1.7"; # "Android Studio Quail 1 | 2026.1.1 RC 2"

View file

@ -408,6 +408,8 @@ stdenv.mkDerivation (finalAttrs: {
(lib.withFeature withSelinux "selinux")
];
__structuredAttrs = true;
env =
lib.optionalAttrs withNativeCompilation {
NATIVE_FULL_AOT = "1";

View file

@ -12,20 +12,20 @@ let
# update-script-start: urls
urls = {
x86_64-linux = {
url = "https://download.jetbrains.com/webstorm/WebStorm-2026.1.2.tar.gz";
hash = "sha256-xoMOLYSuWqWUh5RLI1Q4OnAgGM6tqRDWd1VoULX17yE=";
url = "https://download.jetbrains.com/webstorm/WebStorm-2026.1.3.tar.gz";
hash = "sha256-lryIoVxoytyDyfgjnobQ3e94wIIohmIKL88fwf2I49w=";
};
aarch64-linux = {
url = "https://download.jetbrains.com/webstorm/WebStorm-2026.1.2-aarch64.tar.gz";
hash = "sha256-Qe9yclQj971jK8V2SL5GAlNpgUfTwP/PS2ILv2LyhuE=";
url = "https://download.jetbrains.com/webstorm/WebStorm-2026.1.3-aarch64.tar.gz";
hash = "sha256-oZQaxpVV0O4WlDE6Ia+KzHIF0SfWQBubBFIopSRMbvE=";
};
x86_64-darwin = {
url = "https://download.jetbrains.com/webstorm/WebStorm-2026.1.2.dmg";
hash = "sha256-DmsOaBjKnhbipSrIfbTqNd9+qcd2i3zkKfVvLK1GXbI=";
url = "https://download.jetbrains.com/webstorm/WebStorm-2026.1.3.dmg";
hash = "sha256-KUcQRWYUD/+4HHWnkGuqoltqL2an0WQkUEfUcttwjCI=";
};
aarch64-darwin = {
url = "https://download.jetbrains.com/webstorm/WebStorm-2026.1.2-aarch64.dmg";
hash = "sha256-2YyCHi7yPXnq5KWkqC+Uodnc4xj+DkTsL1vUNqLNmPg=";
url = "https://download.jetbrains.com/webstorm/WebStorm-2026.1.3-aarch64.dmg";
hash = "sha256-1N1RtN1QLvuJ1QL8jbN5TdbOHAHSN84W9XEK2PEPijI=";
};
};
# update-script-end: urls
@ -39,8 +39,8 @@ mkJetBrainsProduct {
product = "WebStorm";
# update-script-start: version
version = "2026.1.2";
buildNumber = "261.24374.125";
version = "2026.1.3";
buildNumber = "261.25134.101";
# update-script-end: version
src = fetchurl (urls.${system} or (throw "Unsupported system: ${system}"));

View file

@ -230,7 +230,7 @@ self: super: {
homepage = "https://github.com/catppuccin/kakoune/";
license = lib.licenses.mit;
platforms = lib.platforms.all;
maintainers = with lib.maintainers; [ philipwilk ];
maintainers = with lib.maintainers; [ jadewilk ];
};
};
}

View file

@ -558,4 +558,25 @@ pkgs.lib.recurseIntoAttrs rec {
EOF
'';
};
nvim_require_check_rtp_no_duplicate = vimUtils.buildVimPlugin {
pname = "neovim-require-check-rtp-no-duplicate-test";
version = "0";
src = runCommandLocal "neovim-require-check-rtp-no-duplicate-src" { } ''
mkdir -p "$out/lua/require-check-rtp-dedup"
cat > "$out/lua/require-check-rtp-dedup/init.lua" <<'EOF'
local target = "lua/require-check-rtp-dedup/init.lua"
local matches = vim.api.nvim_get_runtime_file(target, true)
if #matches ~= 1 then
error(
("expected plugin on runtimepath exactly once, found %d:\n%s"):format(
#matches,
table.concat(matches, "\n")
)
)
end
return {}
EOF
'';
};
}

View file

@ -1,6 +1,6 @@
{
lib,
buildPythonApplication,
python3Packages,
fetchFromGitHub,
gdk-pixbuf,
glib,
@ -9,12 +9,9 @@
gtksourceview,
pango,
webkitgtk_4_1,
pygobject3,
pyyaml,
setuptools,
}:
buildPythonApplication (finalAttrs: {
python3Packages.buildPythonApplication (finalAttrs: {
pname = "rednotebook";
version = "2.42";
pyproject = true;
@ -31,7 +28,7 @@ buildPythonApplication (finalAttrs: {
nativeBuildInputs = [ gobject-introspection ];
build-system = [ setuptools ];
build-system = with python3Packages; [ setuptools ];
propagatedBuildInputs = [
gdk-pixbuf
@ -40,9 +37,11 @@ buildPythonApplication (finalAttrs: {
gtksourceview
pango
webkitgtk_4_1
]
++ (with python3Packages; [
pygobject3
pyyaml
];
]);
makeWrapperArgs = [
"--set GI_TYPELIB_PATH $GI_TYPELIB_PATH"

View file

@ -10,7 +10,6 @@ let
# keep-sorted start
"coc-basedpyright"
"coc-clangd"
"coc-cmake"
"coc-css"
"coc-diagnostic"
"coc-docker"

View file

@ -4043,20 +4043,6 @@ final: prev: {
meta.hydraPlatforms = [ ];
};
coop-nvim = buildVimPlugin {
pname = "coop.nvim";
version = "0-unstable-2026-04-20";
src = fetchFromGitHub {
owner = "gregorias";
repo = "coop.nvim";
rev = "b156e541316aee14be4ae64c93ed8bddb6d03bc1";
hash = "sha256-S6iGmdakI714Im0tetgfASbe0K4/olYsjj26+WP+rSU=";
};
meta.homepage = "https://github.com/gregorias/coop.nvim/";
meta.license = getLicenseFromSpdxId "GPL-3.0-only";
meta.hydraPlatforms = [ ];
};
copilot-cmp = buildVimPlugin {
pname = "copilot-cmp";
version = "0-unstable-2024-12-11";
@ -7085,6 +7071,20 @@ final: prev: {
meta.hydraPlatforms = [ ];
};
guh-nvim = buildVimPlugin {
pname = "guh.nvim";
version = "2026-06-09";
src = fetchFromGitHub {
owner = "justinmk";
repo = "guh.nvim";
rev = "e8d8df780eb13da78a992fdc387bbc5f1401dd6f";
hash = "sha256-vkBP9TvNbUsChtntdgFkHaT01R84HfUAjGXk+SbZUng=";
};
meta.homepage = "https://github.com/justinmk/guh.nvim/";
meta.license = getLicenseFromSpdxId "MIT";
meta.hydraPlatforms = [ ];
};
guihua-lua = buildVimPlugin {
pname = "guihua.lua";
version = "0.1-unstable-2026-05-26";

View file

@ -98,9 +98,9 @@ run_require_checks() {
if [ "$skip" = false ]; then
echo "Attempting to require module: $name"
if @nvimBinary@ -es --headless -n -u NONE -i NONE --clean -V1 \
--cmd "set rtp+=$out,${deps// /,}" \
--cmd "set rtp+=$out,${nativeCheckInputs// /,}" \
--cmd "set rtp+=$out,${checkInputs// /,}" \
--cmd "set rtp+=${deps// /,}" \
--cmd "set rtp+=${nativeCheckInputs// /,}" \
--cmd "set rtp+=${checkInputs// /,}" \
"${luaPathArgs[@]}" \
--cmd "set packpath^=$packPathDir" \
--cmd "packadd testPlugin" \

View file

@ -4154,6 +4154,8 @@ assertNoAdditions {
};
nvimSkipModules = [
# Example seeds and edits a database during require.
"sqlite.examples.bookmarks"
# Require "sql.utils" ?
"sqlite.tbl.cache"
];

View file

@ -287,7 +287,6 @@ https://github.com/niklasdewally/conjure.nvim/,,
https://github.com/wellle/context.vim/,,
https://github.com/Shougo/context_filetype.vim/,,
https://github.com/banjo/contextfiles.nvim/,,
https://github.com/gregorias/coop.nvim/,,
https://github.com/zbirenbaum/copilot-cmp/,,
https://github.com/copilotlsp-nvim/copilot-lsp/,,
https://github.com/AndreM222/copilot-lualine/,,
@ -504,6 +503,7 @@ https://github.com/ellisonleao/gruvbox.nvim/,,
https://github.com/nvimdev/guard-collection/,,
https://github.com/nvimdev/guard.nvim/,,
https://github.com/nmac427/guess-indent.nvim/,,
https://github.com/justinmk/guh.nvim/,main,
https://github.com/ray-x/guihua.lua/,,
https://github.com/sjl/gundo.vim/,,
https://github.com/junegunn/gv.vim/,,

View file

@ -21,26 +21,26 @@ vscode-utils.buildVscodeMarketplaceExtension (finalAttrs: {
sources = {
"x86_64-linux" = {
arch = "linux-x64";
hash = "sha256-q5j8Ip/ew3oHGIakJm/CTeKcW4O9FR062f4rILXbQrQ=";
hash = "sha256-R3ab2IeY9QnDhZFk52/05pIv4A+sZU3kJ9Jn5uLRa4Y=";
};
"aarch64-linux" = {
arch = "linux-arm64";
hash = "sha256-viZxHDA8SfsIVB5R9I/8SB8EEWRvt1kpZPDA4w0sD54=";
hash = "sha256-AE6zS8bJ4vec+P36NkxWYQ1tmcJG2WsFkv75+gRlrxA=";
};
"x86_64-darwin" = {
arch = "darwin-x64";
hash = "sha256-kL0bab7BT45EEh17jKFVHqaMQEYkxLlsDKtK1deoS4M=";
hash = "sha256-n1qV1Lrl65HSDthMc5/7hLppeNBO6067Z+Rf5+kxfnA=";
};
"aarch64-darwin" = {
arch = "darwin-arm64";
hash = "sha256-fJW6fTGRWLBWB1yZ1pGb3p4KkFLhrDXqw+0wjOv71Vo=";
hash = "sha256-g+lkUYym43o8cEFseWCrcSUUTx296u8DS9JvnU1dBLU=";
};
};
in
{
name = "claude-code";
publisher = "anthropic";
version = "2.1.161";
version = "2.1.172";
}
// sources.${stdenvNoCC.hostPlatform.system}
or (throw "Unsupported system ${stdenvNoCC.hostPlatform.system}");

View file

@ -12,26 +12,26 @@ vscode-utils.buildVscodeMarketplaceExtension {
sources = {
"x86_64-linux" = {
arch = "linux-x64";
hash = "sha256-l1bwzuEi8sCBsdad2a5UDPN12QtlHhhgXBfsNxP5GwA=";
hash = "sha256-qXGdFXx8Vr9oVStBjaeWEsQjhQUbp9MnLUfJtyhgwkA=";
};
"x86_64-darwin" = {
arch = "darwin-x64";
hash = "sha256-31Sj5KlZnRKa0sR2J4A4CRuDF8fwXlzikukH+OX/GpU=";
hash = "sha256-Bd3ool5Ep8I5iiTMUHF48r/sf3F1dROx6Umns0pDGzE=";
};
"aarch64-linux" = {
arch = "linux-arm64";
hash = "sha256-7m9or/105/YIjhMlwMFLcN9tP9hj/4NU85Y3/5DDuDw=";
hash = "sha256-Jv2lrdvY0tthJM2fifUlCW/QBzEZAa8QDHj4o+a4KxY=";
};
"aarch64-darwin" = {
arch = "darwin-arm64";
hash = "sha256-q7mGGv/L9N7hwM0EIKF7d+lxcl0V00a6I/CK8j5E8SE=";
hash = "sha256-VooAoTyysIFH1AaidcoRs7fJpID0LbmWnLdivGZfdrM=";
};
};
in
{
name = "ruff";
publisher = "charliermarsh";
version = "2026.46.0";
version = "2026.48.0";
}
// sources.${stdenvNoCC.hostPlatform.system}
or (throw "Unsupported system ${stdenvNoCC.hostPlatform.system}");

View file

@ -1145,8 +1145,8 @@ let
mktplcRef = {
name = "vscode-database-client2";
publisher = "cweijan";
version = "8.4.7";
hash = "sha256-HiyLNVtdJgzwn6BSR4ojHOkLrQ49PhRQa4twst69j7Q=";
version = "8.5.1";
hash = "sha256-adpTh4tNlWFdAGultTLZ9SfN9sMOpMaMWq8x6diGzBg=";
};
meta = {
description = "Database Client For Visual Studio Code";
@ -1215,8 +1215,8 @@ let
mktplcRef = {
name = "flutter";
publisher = "dart-code";
version = "3.134.0";
hash = "sha256-wWANxBh9Tg3VkfbcrR0NgOIC8cpeZJCNfOevT7E4zKY=";
version = "3.136.0";
hash = "sha256-RK493qHjEK/k8oy/OIzO3bvNnFo+ZftQhZMIAmBCaJk=";
};
meta.license = lib.licenses.mit;
@ -1375,8 +1375,8 @@ let
mktplcRef = {
name = "competitive-programming-helper";
publisher = "DivyanshuAgrawal";
version = "2026.5.1779885478";
hash = "sha256-8RU6JWHeYOAxof2TXpeXlXSVD9+0e0E46X85A11l4Pk=";
version = "2026.6.1780508121";
hash = "sha256-4kb7Nk+gctECMQM/cko+q1Bta1EKPXPEqyCQLBMkbEo=";
};
meta = {
changelog = "https://marketplace.visualstudio.com/items/DivyanshuAgrawal.competitive-programming-helper/changelog";
@ -2052,8 +2052,8 @@ let
mktplcRef = {
name = "gc-excelviewer";
publisher = "grapecity";
version = "4.2.65";
hash = "sha256-MDGwoecU8km6a7NVQjPRqJ43592Bftd/nXj1cdBB+V8=";
version = "4.2.66";
hash = "sha256-EFy5GjkgV5puELTzNat+30WC224PLDQJmJeBILc1Fvo=";
};
meta = {
description = "Edit Excel spreadsheets and CSV files in Visual Studio Code and VS Code for the Web";
@ -2324,8 +2324,8 @@ let
mktplcRef = {
name = "vscode-vibrancy-continued";
publisher = "illixion";
version = "1.1.77";
hash = "sha256-khxajorzIntF2YwKVd6aZ1+doEr2oN4OliBpc7TTB7o=";
version = "1.1.78";
hash = "sha256-w0i0Gmqj2atCtEYW+0XvZech37qQaTjkt8MJRdHamxs=";
};
meta = {
downloadPage = "https://marketplace.visualstudio.com/items?itemName=illixion.vscode-vibrancy-continued";
@ -3865,8 +3865,8 @@ let
mktplcRef = {
name = "ansible";
publisher = "redhat";
version = "26.5.0";
hash = "sha256-gKe+LC0OOZi5I1Tei64JtQUR1gpWyTex9MoLGT6jgWU=";
version = "26.6.0";
hash = "sha256-pCq9niQQIBkiJL6q90W6ecSkGPHttiOAECAyrPgBeqg=";
};
meta = {
description = "Ansible language support";
@ -3997,8 +3997,8 @@ let
mktplcRef = {
name = "wikitext";
publisher = "RoweWilsonFrederiskHolme";
version = "4.0.4";
hash = "sha256-IEnWoRQfWx4TbysSQlOxG0c8kiqtR4fOhaV1JxDkRCw=";
version = "4.0.5";
hash = "sha256-VyrcgS93B5Xd4s101lnTw9o27ffcvmxkShCKJ+6H+2w=";
};
meta = {
description = "Extension that helps users view and write MediaWiki's Wikitext files";

View file

@ -7,8 +7,8 @@ vscode-utils.buildVscodeMarketplaceExtension rec {
mktplcRef = {
name = "oracle-java";
publisher = "oracle";
version = "25.1.0";
hash = "sha256-HjDSlvG6khFJA/3ukjVCrPqbQiAKVhgP6KcCfC/jUrc=";
version = "26.0.0";
hash = "sha256-SfZMOiSuABXcYVi4CmxUUUIeAUmp6s45jjyy4HXoe24=";
};
meta = {

View file

@ -7,19 +7,19 @@
let
supported = {
x86_64-linux = {
hash = "sha256-HECflrFni3eWxMs+BpjWBhU3pqF5jjMIEjkp9ibx784=";
hash = "sha256-7uZZuEijg1Aca1h2NrBwFU9iFnfhG17waXxqVlAKzTA=";
arch = "linux-x64";
};
x86_64-darwin = {
hash = "sha256-dCkSOClWWq3DGU9psrinI5f5oC69K+AhdHdXwKIQsFw=";
hash = "sha256-NPh3Sxf2k/Nj4vt0SGsa5H6QOI1IBaL20g79Pl4/iPg=";
arch = "darwin-x64";
};
aarch64-linux = {
hash = "sha256-XNIx2ibOe1/1lo8RkYkAv+oBDYpqnmMcIjpoulbrr+w=";
hash = "sha256-tszcouaPUGTH1MpGJfU0RiLvBN7qIhH1vC3XiAmHFK8=";
arch = "linux-arm64";
};
aarch64-darwin = {
hash = "sha256-rXVuQN0SDmymQNncFZzyD4H+j6hxp1yoiaNXnbzrlo0=";
hash = "sha256-JV9pZqq48q00GM0cg3Wu00qrrkn2fczbHBVqnCM84lI=";
arch = "darwin-arm64";
};
};
@ -34,7 +34,7 @@ vscode-utils.buildVscodeMarketplaceExtension {
mktplcRef = base // {
name = "tombi";
publisher = "tombi-toml";
version = "1.1.1";
version = "1.1.2";
};
meta = {
description = "TOML Language Server";

View file

@ -37,6 +37,9 @@ vscode-utils.buildVscodeMarketplaceExtension {
// sources.${stdenvNoCC.hostPlatform.system}
or (throw "Unsupported system ${stdenvNoCC.hostPlatform.system}");
__structuredAttrs = true;
strictDeps = true;
nativeBuildInputs = lib.optionals stdenv.hostPlatform.isLinux [
autoPatchelfHook
];

View file

@ -10,8 +10,8 @@ vscode-utils.buildVscodeMarketplaceExtension {
mktplcRef = {
name = "wgsl-analyzer";
publisher = "wgsl-analyzer";
version = "0.11.262";
hash = "sha256-a2TVwTmxP9wBt0tMkQcVCyzM0RoihGag56ITd+xjtl8=";
version = "0.11.318";
hash = "sha256-px6lKME6aapi9L9Owb3zhbEMoKmA9GpBQrHtb8Kg0XI=";
};
nativeBuildInputs = [

View file

@ -35,17 +35,17 @@ let
hash =
{
x86_64-linux = "sha256-t26YN3E5XaSJ7gki8nm06hVh4ZvXDEU77M749ZrqfAo=";
x86_64-darwin = "sha256-jOnwhiDJmU+EqU30wg1+frqDDxJgfngETx414i2YTIg=";
aarch64-linux = "sha256-8sYanI12qDMPgVG7S0QKLEkU0i/SICkJ5wz/OwhP+i4=";
aarch64-darwin = "sha256-oXeZZWAvpUn5KItEOR8yX9iQ0Fp6EzXGux0jvYbZqtU=";
armv7l-linux = "sha256-16cUu1C389edf0aHxXxTLJwjxmpHxM8mv1YFnPDLgP4=";
x86_64-linux = "sha256-L975R3F779LgaFTL4B6ZtImPd1LyXhImnDgCPmO5PI8=";
x86_64-darwin = "sha256-Sygw/VkIiyV+iABylgFpTiHs0f5dS6NYPWSm5BNh9tQ=";
aarch64-linux = "sha256-jcFC668WKAjlYju33RI6poAKnhm3fL1hO16alUwjwv4=";
aarch64-darwin = "sha256-AY6WeDzGEH5zXRosN1H/osxC3e5j0Hs9s2Ys2xe1UxI=";
armv7l-linux = "sha256-LqofnnZid/I0lVTyhC7yHD+Fxz4dSBxKJ8n+lp2uucQ=";
}
.${system} or throwSystem;
# Please backport all compatible updates to the stable release.
# This is important for the extension ecosystem.
version = "1.122.1";
version = "1.123.0";
# The update server (update.code.visualstudio.com) expects the version path
# segment in X.Y.Z form, so we normalize X.Y to X.Y.0 (e.g. "1.110" → "1.110.0").
@ -53,7 +53,7 @@ let
downloadVersion = lib.versions.pad 3 version;
# This is used for VS Code - Remote SSH test
rev = "8761a5560cfd65fdd19ce7e2bd18dab5c0a4d84e";
rev = "6a44c352bd24569c417e530095901b649960f9f8";
in
buildVscode {
pname = "vscode" + lib.optionalString isInsiders "-insiders";
@ -86,7 +86,7 @@ buildVscode {
src = fetchurl {
name = "vscode-server-${rev}.tar.gz";
url = "https://update.code.visualstudio.com/commit:${rev}/server-linux-x64/stable";
hash = "sha256-7n8KvIYEDYO8qqJjfbuUsgUwCxq9FJ6i/EuDBd1HQDk=";
hash = "sha256-i034bIsaPlxlVFNY5cKf/ftWPy17SFokbFUMa+zeLng=";
};
stdenv = stdenvNoCC;
};
@ -113,6 +113,9 @@ buildVscode {
'';
homepage = "https://code.visualstudio.com/";
downloadPage = "https://code.visualstudio.com/Updates";
changelog = "https://code.visualstudio.com/updates/v${
lib.replaceString "." "_" (lib.versions.majorMinor version)
}";
license = lib.licenses.unfree;
maintainers = with lib.maintainers; [
eadwu

View file

@ -79,6 +79,7 @@ buildVscode rec {
'';
homepage = "https://github.com/VSCodium/vscodium";
downloadPage = "https://github.com/VSCodium/vscodium/releases";
changelog = "https://github.com/VSCodium/vscodium/releases/tag/${version}";
license = lib.licenses.mit;
sourceProvenance = with lib.sourceTypes; [ binaryNativeCode ];
maintainers = with lib.maintainers; [

View file

@ -1,54 +0,0 @@
{
cmake,
pkg-config,
callPackage,
gobject-introspection,
wrapGAppsHook3,
python3Packages,
libxml2,
gnuplot,
adwaita-icon-theme,
gdk-pixbuf,
intltool,
libmirage,
}:
python3Packages.buildPythonApplication {
inherit
(callPackage ./common-drv-attrs.nix {
version = "3.2.6";
pname = "image-analyzer";
hash = "sha256-7I8RUgd+k3cEzskJGbziv1f0/eo5QQXn62wGh/Y5ozc=";
})
pname
version
src
meta
;
buildInputs = [
libxml2
gnuplot
libmirage
adwaita-icon-theme
gdk-pixbuf
];
propagatedBuildInputs = with python3Packages; [
pygobject3
matplotlib
];
nativeBuildInputs = [
cmake
pkg-config
wrapGAppsHook3
intltool
gobject-introspection
];
pyproject = false;
dontWrapGApps = true;
preFixup = ''
makeWrapperArgs+=("''${gappsWrapperArgs[@]}")
'';
}

View file

@ -1,42 +0,0 @@
{
callPackage,
python3Packages,
cmake,
pkg-config,
intltool,
wrapGAppsNoGuiHook,
gobject-introspection,
}:
python3Packages.buildPythonApplication {
inherit
(callPackage ./common-drv-attrs.nix {
version = "3.2.5";
pname = "cdemu-client";
hash = "sha256-py2F61v8vO0BCM18GCflAiD48deZjbMM6wqoCDZsOd8=";
})
pname
version
src
meta
;
nativeBuildInputs = [
cmake
pkg-config
intltool
wrapGAppsNoGuiHook
gobject-introspection
];
propagatedBuildInputs = with python3Packages; [
dbus-python
pygobject3
];
pyproject = false;
dontWrapGApps = true;
preFixup = ''
makeWrapperArgs+=("''${gappsWrapperArgs[@]}")
'';
}

View file

@ -1,33 +0,0 @@
{
lib,
fetchurl,
pname,
version,
hash,
}:
{
inherit pname version;
src = fetchurl {
url = "mirror://sourceforge/cdemu/${pname}-${version}.tar.xz";
inherit hash;
};
meta = {
description = "Suite of tools for emulating optical drives and discs";
longDescription = ''
CDEmu consists of:
- a kernel module implementing a virtual drive-controller
- libmirage which is a software library for interpreting optical disc images
- a daemon which emulates the functionality of an optical drive+disc
- textmode and GTK clients for controlling the emulator
- an image analyzer to view the structure of image files
Optical media emulated by CDemu can be mounted within Linux. Automounting is also allowed.
'';
homepage = "https://cdemu.sourceforge.io/";
license = lib.licenses.gpl2Plus;
platforms = lib.platforms.linux;
maintainers = with lib.maintainers; [ bendlas ];
};
}

View file

@ -1,59 +0,0 @@
{
stdenv,
callPackage,
cmake,
pkg-config,
glib,
libao,
intltool,
libmirage,
coreutils,
}:
let
inherit
(callPackage ./common-drv-attrs.nix {
version = "3.2.7";
pname = "cdemu-daemon";
hash = "sha256-EKh2G6RA9Yq46BpTAqN2s6TpLJb8gwDuEpGiwdGcelc=";
})
pname
version
src
meta
;
in
stdenv.mkDerivation {
inherit pname version src;
nativeBuildInputs = [
cmake
pkg-config
intltool
];
buildInputs = [
glib
libao
libmirage
];
postInstall = ''
mkdir -p $out/share/dbus-1/services
cp -R ../service-example $out/share/cdemu
substitute \
$out/share/cdemu/net.sf.cdemu.CDEmuDaemon.service \
$out/share/dbus-1/services/net.sf.cdemu.CDEmuDaemon.service \
--replace /bin/true ${coreutils}/bin/true
'';
meta = {
inherit (meta)
description
license
longDescription
maintainers
platforms
;
homepage = "https://cdemu.sourceforge.io/about/daemon/";
mainProgram = "cdemu-daemon";
};
}

View file

@ -1,49 +0,0 @@
{
callPackage,
cmake,
pkg-config,
wrapGAppsHook3,
gobject-introspection,
python3Packages,
libnotify,
intltool,
adwaita-icon-theme,
gdk-pixbuf,
libappindicator-gtk3,
}:
python3Packages.buildPythonApplication {
inherit
(callPackage ./common-drv-attrs.nix {
version = "3.2.6";
pname = "gcdemu";
hash = "sha256-w4vzKoSotL5Cjfr4Cu4YhNSWXJqS+n/vySrwvbhR1zA=";
})
pname
version
src
meta
;
nativeBuildInputs = [
cmake
pkg-config
wrapGAppsHook3
intltool
gobject-introspection
];
buildInputs = [
libnotify
adwaita-icon-theme
gdk-pixbuf
libappindicator-gtk3
];
propagatedBuildInputs = with python3Packages; [ pygobject3 ];
pyproject = false;
dontWrapGApps = true;
preFixup = ''
makeWrapperArgs+=("''${gappsWrapperArgs[@]}")
'';
}

View file

@ -1,73 +0,0 @@
{
stdenv,
callPackage,
cmake,
pkg-config,
gobject-introspection,
vala,
glib,
libsndfile,
zlib,
bzip2,
xz,
libsamplerate,
intltool,
pcre,
util-linux,
libselinux,
libsepol,
}:
let
inherit
(callPackage ./common-drv-attrs.nix {
version = "3.2.10";
pname = "libmirage";
hash = "sha256-+T5Gu3VcprCkSJcq/kTySRnNI7nc+GbRtctLkzPhgK4=";
})
pname
version
src
meta
;
in
stdenv.mkDerivation {
inherit pname version src;
env = {
PKG_CONFIG_GOBJECT_INTROSPECTION_1_0_GIRDIR = "${placeholder "out"}/share/gir-1.0";
PKG_CONFIG_GOBJECT_INTROSPECTION_1_0_TYPELIBDIR = "${placeholder "out"}/lib/girepository-1.0";
};
buildInputs = [
glib
libsndfile
zlib
bzip2
xz
libsamplerate
];
nativeBuildInputs = [
cmake
pkg-config
intltool
gobject-introspection
vala
];
propagatedBuildInputs = [
pcre
util-linux
libselinux
libsepol
];
meta = {
inherit (meta)
maintainers
license
platforms
;
description = "CD-ROM image access library";
homepage = "https://cdemu.sourceforge.io/about/libmirage/";
};
}

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