Self-host your own digital island

nixfmt

+10 -8
flake.nix
···
outputs = { self, nixpkgs, nixos-mailserver, ... }: rec {
packages = nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed (system:
-
let
-
pkgs = nixpkgs.legacyPackages.${system};
-
in {
-
manpage = import ./man { inherit pkgs system nixos-mailserver; };
-
});
+
let pkgs = nixpkgs.legacyPackages.${system};
+
in { manpage = import ./man { inherit pkgs system nixos-mailserver; }; });
nixosModules.default = {
imports = [
./modules/default.nix
nixos-mailserver.nixosModule
({ pkgs, config, ... }: {
-
nixpkgs.overlays = [ (final: prev: {
-
mautrix-meta = (prev.callPackage ./pkgs/mautrix-meta.nix { });
-
}) ];
+
nixpkgs.overlays = [
+
(final: prev: {
+
mautrix-meta = (prev.callPackage ./pkgs/mautrix-meta.nix { });
+
})
+
];
})
];
};
defaultTemplate.path = ./template;
+
+
formatter = nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed
+
(system: nixpkgs.legacyPackages.${system}.nixfmt);
};
}
+28 -36
man/default.nix
···
with pkgs;
let
-
optionsDoc =
-
let
-
eval = import (pkgs.path + "/nixos/lib/eval-config.nix") {
-
inherit system;
-
modules = [
-
../modules/default.nix
-
nixos-mailserver
-
];
-
};
-
in pkgs.nixosOptionsDoc {
-
options = eval.options;
-
# TODO make sure all options have descriptions
-
warningsAreErrors = false;
-
};
+
optionsDoc = let
+
eval = import (pkgs.path + "/nixos/lib/eval-config.nix") {
+
inherit system;
+
modules = [ ../modules/default.nix nixos-mailserver ];
+
};
+
in pkgs.nixosOptionsDoc {
+
options = eval.options;
+
# TODO make sure all options have descriptions
+
warningsAreErrors = false;
+
};
# Generate the `man eliean.nix` package
-
eilean-configuration-manual =
-
runCommand "eilean-reference-manpage"
-
{ nativeBuildInputs = [
-
buildPackages.installShellFiles
-
buildPackages.nixos-render-docs
-
];
-
allowedReferences = [ "out" ];
-
}
-
''
-
# Generate manpages.
-
mkdir -p $out/share/man/man5
-
# filter to only eilean options
-
cat ${optionsDoc.optionsJSON}/share/doc/nixos/options.json \
-
| ${pkgs.jq}/bin/jq 'with_entries(select(.key | test("^eilean")))' \
-
> eilean-options.json
-
nixos-render-docs -j $NIX_BUILD_CORES options manpage \
-
--revision dev \
-
--header ${./eilean-configuration-nix-header.5} \
-
--footer ${./eilean-configuration-nix-footer.5} \
-
eilean-options.json \
-
$out/share/man/man5/eilean-configuration.nix.5
-
'';
+
eilean-configuration-manual = runCommand "eilean-reference-manpage" {
+
nativeBuildInputs =
+
[ buildPackages.installShellFiles buildPackages.nixos-render-docs ];
+
allowedReferences = [ "out" ];
+
} ''
+
# Generate manpages.
+
mkdir -p $out/share/man/man5
+
# filter to only eilean options
+
cat ${optionsDoc.optionsJSON}/share/doc/nixos/options.json \
+
| ${pkgs.jq}/bin/jq 'with_entries(select(.key | test("^eilean")))' \
+
> eilean-options.json
+
nixos-render-docs -j $NIX_BUILD_CORES options manpage \
+
--revision dev \
+
--header ${./eilean-configuration-nix-header.5} \
+
--footer ${./eilean-configuration-nix-footer.5} \
+
eilean-options.json \
+
$out/share/man/man5/eilean-configuration.nix.5
+
'';
in eilean-configuration-manual
+7 -15
modules/default.nix
···
];
options.eilean = with types; {
-
username = mkOption {
-
type = str;
-
};
-
serverIpv4 = mkOption {
-
type = str;
-
};
-
serverIpv6 = mkOption {
-
type = str;
-
};
-
publicInterface = mkOption {
-
type = str;
-
};
+
username = mkOption { type = str; };
+
serverIpv4 = mkOption { type = str; };
+
serverIpv6 = mkOption { type = str; };
+
publicInterface = mkOption { type = str; };
};
config = {
# TODO install manpage
-
environment.systemPackages = [
-
];
-
security.acme.defaults.email = "${config.eilean.username}@${config.networking.domain}";
+
environment.systemPackages = [ ];
+
security.acme.defaults.email =
+
"${config.eilean.username}@${config.networking.domain}";
networking.firewall.allowedTCPPorts = mkIf config.services.nginx.enable [
80 # HTTP
443 # HTTPS
+6 -7
modules/dns.nix
···
{ config, lib, ... }:
with lib;
-
let cfg = config.eilean; in
-
{
-
+
let cfg = config.eilean;
+
in {
+
options.eilean.dns = {
enable = mkEnableOption "dns";
nameservers = mkOption {
···
default = [ "ns1" "ns2" ];
};
};
-
+
config.eilean.services.dns = mkIf cfg.dns.enable {
enable = true;
zones.${config.networking.domain} = {
···
type = "AAAA";
data = cfg.serverIpv6;
}
-
]) cfg.dns.nameservers ++
-
[
+
]) cfg.dns.nameservers ++ [
{
name = "@";
type = "A";
···
type = "AAAA";
data = cfg.serverIpv6;
}
-
+
{
name = "@";
type = "LOC";
+22 -17
modules/gitea.nix
···
enableACME = true;
forceSSL = true;
locations."/" = {
-
proxyPass = "http://localhost:${builtins.toString config.services.gitea.settings.server.HTTP_PORT}/";
+
proxyPass = "http://localhost:${
+
builtins.toString config.services.gitea.settings.server.HTTP_PORT
+
}/";
};
};
};
···
RestrictRealtime = mkForce false;
RestrictSUIDSGID = mkForce false;
SystemCallArchitectures = mkForce "";
-
SystemCallFilter = mkForce [];
+
SystemCallFilter = mkForce [ ];
};
eilean.dns.enable = true;
-
eilean.services.dns.zones.${config.networking.domain}.records = [
-
{
-
name = "git";
-
type = "CNAME";
-
data = "vps";
-
}
-
];
+
eilean.services.dns.zones.${config.networking.domain}.records = [{
+
name = "git";
+
type = "CNAME";
+
data = "vps";
+
}];
# proxy port 22 on ethernet interface to internal gitea ssh server
# openssh server remains accessible on port 22 via vpn(s)
···
};
networking.firewall = {
-
allowedTCPPorts = [
-
22
-
cfg.gitea.sshPort
-
];
+
allowedTCPPorts = [ 22 cfg.gitea.sshPort ];
extraCommands = ''
# proxy all traffic on public interface to the gitea SSH server
-
iptables -A PREROUTING -t nat -i ${config.eilean.publicInterface} -p tcp --dport 22 -j REDIRECT --to-port ${builtins.toString cfg.gitea.sshPort}
-
ip6tables -A PREROUTING -t nat -i ${config.eilean.publicInterface} -p tcp --dport 22 -j REDIRECT --to-port ${builtins.toString cfg.gitea.sshPort}
+
iptables -A PREROUTING -t nat -i ${config.eilean.publicInterface} -p tcp --dport 22 -j REDIRECT --to-port ${
+
builtins.toString cfg.gitea.sshPort
+
}
+
ip6tables -A PREROUTING -t nat -i ${config.eilean.publicInterface} -p tcp --dport 22 -j REDIRECT --to-port ${
+
builtins.toString cfg.gitea.sshPort
+
}
# proxy locally originating outgoing packets
-
iptables -A OUTPUT -d ${config.eilean.serverIpv4} -t nat -p tcp --dport 22 -j REDIRECT --to-port ${builtins.toString cfg.gitea.sshPort}
-
ip6tables -A OUTPUT -d ${config.eilean.serverIpv6} -t nat -p tcp --dport 22 -j REDIRECT --to-port ${builtins.toString cfg.gitea.sshPort}
+
iptables -A OUTPUT -d ${config.eilean.serverIpv4} -t nat -p tcp --dport 22 -j REDIRECT --to-port ${
+
builtins.toString cfg.gitea.sshPort
+
}
+
ip6tables -A OUTPUT -d ${config.eilean.serverIpv6} -t nat -p tcp --dport 22 -j REDIRECT --to-port ${
+
builtins.toString cfg.gitea.sshPort
+
}
'';
};
+8 -14
modules/headscale.nix
···
{ pkgs, config, lib, ... }:
with lib;
-
let
-
cfg = config.eilean;
+
let cfg = config.eilean;
in {
options.eilean.headscale = with lib; {
enable = mkEnableOption "headscale";
···
domain = mkOption {
type = types.str;
default = "headscale.${config.networking.domain}";
-
defaultText = "headscale.$${config.networking.domain}";
+
defaultText = "headscale.$\${config.networking.domain}";
};
};
···
settings = {
server_url = "https://${cfg.headscale.domain}";
logtail.enabled = false;
-
ip_prefixes = [
-
"100.64.0.0/10"
-
"fd7a:115c:a1e0::/48"
-
];
+
ip_prefixes = [ "100.64.0.0/10" "fd7a:115c:a1e0::/48" ];
dns_config = {
# magicDns = true;
nameservers = config.networking.nameservers;
···
environment.systemPackages = [ config.services.headscale.package ];
eilean.dns.enable = true;
-
eilean.services.dns.zones.${cfg.headscale.zone}.records = [
-
{
-
name = "${cfg.headscale.domain}.";
-
type = "CNAME";
-
data = "vps";
-
}
-
];
+
eilean.services.dns.zones.${cfg.headscale.zone}.records = [{
+
name = "${cfg.headscale.domain}.";
+
type = "CNAME";
+
data = "vps";
+
}];
};
}
+12 -7
modules/mailserver.nix
···
'';
services.postfix.config = {
-
smtpd_tls_protocols = mkForce "TLSv1.3, TLSv1.2, !TLSv1.1, !TLSv1, !SSLv2, !SSLv3";
-
smtp_tls_protocols = mkForce "TLSv1.3, TLSv1.2, !TLSv1.1, !TLSv1, !SSLv2, !SSLv3";
-
smtpd_tls_mandatory_protocols = mkForce "TLSv1.3, !TLSv1.2, TLSv1.1, !TLSv1, !SSLv2, !SSLv3";
-
smtp_tls_mandatory_protocols = mkForce "TLSv1.3, !TLSv1.2, TLSv1.1, !TLSv1, !SSLv2, !SSLv3";
+
smtpd_tls_protocols =
+
mkForce "TLSv1.3, TLSv1.2, !TLSv1.1, !TLSv1, !SSLv2, !SSLv3";
+
smtp_tls_protocols =
+
mkForce "TLSv1.3, TLSv1.2, !TLSv1.1, !TLSv1, !SSLv2, !SSLv3";
+
smtpd_tls_mandatory_protocols =
+
mkForce "TLSv1.3, !TLSv1.2, TLSv1.1, !TLSv1, !SSLv2, !SSLv3";
+
smtp_tls_mandatory_protocols =
+
mkForce "TLSv1.3, !TLSv1.2, TLSv1.1, !TLSv1, !SSLv2, !SSLv3";
};
eilean.dns.enable = true;
···
{
name = "@";
type = "TXT";
-
data = "\"v=spf1 a:mail.${config.networking.domain} -all\"";
+
data = ''"v=spf1 a:mail.${config.networking.domain} -all"'';
}
{
name = "mail._domainkey";
ttl = 10800;
type = "TXT";
-
data = "\"v=DKIM1; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6YmYYvoFF7VqtGcozpVQa78aaGgZdvc5ZIHqzmkKdCBEyDF2FRbCEK4s2AlC8hhc8O4mSSe3S4AzEhlRgHXbU22GBaUZ3s2WHS8JJwZvWeTjsbXQwjN/U7xpkqXPHLH9IVfOJbHlp4HQmCAXw4NaypgkkxIGK0jaZHm2j6/1izQIDAQAB\"";
+
data = ''
+
"v=DKIM1; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6YmYYvoFF7VqtGcozpVQa78aaGgZdvc5ZIHqzmkKdCBEyDF2FRbCEK4s2AlC8hhc8O4mSSe3S4AzEhlRgHXbU22GBaUZ3s2WHS8JJwZvWeTjsbXQwjN/U7xpkqXPHLH9IVfOJbHlp4HQmCAXw4NaypgkkxIGK0jaZHm2j6/1izQIDAQAB"'';
}
{
name = "_dmarc";
ttl = 10800;
type = "TXT";
-
data = "\"v=DMARC1; p=reject\"";
+
data = ''"v=DMARC1; p=reject"'';
}
];
};
+15 -18
modules/mastodon.nix
···
cfg = config.eilean;
domain = config.networking.domain;
in {
-
options.eilean.mastodon = {
-
enable = mkEnableOption "mastodon";
-
};
+
options.eilean.mastodon = { enable = mkEnableOption "mastodon"; };
config = mkIf cfg.mastodon.enable {
services.mastodon = {
···
WEB_DOMAIN = "mastodon.${domain}";
# https://peterbabic.dev/blog/setting-up-smtp-in-mastodon/
-
SMTP_SSL="true";
-
SMTP_ENABLE_STARTTLS="false";
-
SMTP_OPENSSL_VERIFY_MODE="none";
+
SMTP_SSL = "true";
+
SMTP_ENABLE_STARTTLS = "false";
+
SMTP_OPENSSL_VERIFY_MODE = "none";
};
};
-
users.groups.${config.services.mastodon.group}.members = [ config.services.nginx.user ];
+
users.groups.${config.services.mastodon.group}.members =
+
[ config.services.nginx.user ];
services.nginx = {
enable = true;
···
locations."/system/".alias = "/var/lib/mastodon/public-system/";
-
locations."/" = {
-
tryFiles = "$uri @proxy";
-
};
+
locations."/" = { tryFiles = "$uri @proxy"; };
locations."@proxy" = {
-
proxyPass = "http://127.0.0.1:${builtins.toString config.services.mastodon.webPort}";
+
proxyPass = "http://127.0.0.1:${
+
builtins.toString config.services.mastodon.webPort
+
}";
proxyWebsockets = true;
};
};
···
};
eilean.dns.enable = true;
-
eilean.services.dns.zones.${config.networking.domain}.records = [
-
{
-
name = "mastodon";
-
type = "CNAME";
-
data = "vps";
-
}
-
];
+
eilean.services.dns.zones.${config.networking.domain}.records = [{
+
name = "mastodon";
+
type = "CNAME";
+
data = "vps";
+
}];
};
}
+25 -29
modules/matrix/mautrix-instagram.nix
···
-
{
-
lib,
-
config,
-
pkgs,
-
...
-
}: let
+
{ lib, config, pkgs, ... }:
+
let
cfg = config.services.mautrix-instagram;
dataDir = "/var/lib/mautrix-instagram";
registrationFile = "${dataDir}/instagram-registration.yaml";
settingsFile = "${dataDir}/config.json";
-
settingsFileUnsubstituted = settingsFormat.generate "mautrix-instagram-config-unsubstituted.json" cfg.settings;
-
settingsFormat = pkgs.formats.json {};
+
settingsFileUnsubstituted =
+
settingsFormat.generate "mautrix-instagram-config-unsubstituted.json"
+
cfg.settings;
+
settingsFormat = pkgs.formats.json { };
appservicePort = 29319;
mkDefaults = lib.mapAttrsRecursive (n: v: lib.mkDefault v);
···
};
bridge = {
username_template = "instagram_{{.}}";
-
double_puppet_server_map = {};
-
login_shared_secret_map = {};
+
double_puppet_server_map = { };
+
login_shared_secret_map = { };
permissions."*" = "relay";
relay.enabled = true;
};
···
in {
options.services.mautrix-instagram = {
-
enable = lib.mkEnableOption (lib.mdDoc "mautrix-instagram, a puppeting/relaybot bridge between Matrix and Instagram.");
+
enable = lib.mkEnableOption (lib.mdDoc
+
"mautrix-instagram, a puppeting/relaybot bridge between Matrix and Instagram.");
settings = lib.mkOption {
type = settingsFormat.type;
···
ephemeral_events = false;
};
bridge = {
-
history_sync = {
-
request_full_sync = true;
-
};
+
history_sync = { request_full_sync = true; };
private_chat_portal_meta = true;
mute_bridging = true;
encryption = {
···
default = true;
require = true;
};
-
provisioning = {
-
shared_secret = "disable";
-
};
-
permissions = {
-
"example.com" = "user";
-
};
+
provisioning = { shared_secret = "disable"; };
+
permissions = { "example.com" = "user"; };
};
};
};
serviceDependencies = lib.mkOption {
type = with lib.types; listOf str;
-
default = lib.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit;
+
default = lib.optional config.services.matrix-synapse.enable
+
config.services.matrix-synapse.serviceUnit;
defaultText = lib.literalExpression ''
optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnits
'';
···
description = "Mautrix-Instagram bridge user";
};
-
users.groups.mautrix-instagram = {};
+
users.groups.mautrix-instagram = { };
services.mautrix-instagram.settings = lib.mkMerge (map mkDefaults [
defaultConfig
# Note: this is defined here to avoid the docs depending on `config`
-
{ homeserver.domain = config.services.matrix-synapse.settings.server_name; }
+
{
+
homeserver.domain = config.services.matrix-synapse.settings.server_name;
+
}
]);
systemd.services.mautrix-instagram = {
description = "Mautrix-Instagram Service - A Instagram bridge for Matrix";
-
wantedBy = ["multi-user.target"];
-
wants = ["network-online.target"] ++ cfg.serviceDependencies;
-
after = ["network-online.target"] ++ cfg.serviceDependencies;
+
wantedBy = [ "multi-user.target" ];
+
wants = [ "network-online.target" ] ++ cfg.serviceDependencies;
+
after = [ "network-online.target" ] ++ cfg.serviceDependencies;
preStart = ''
# substitute the settings file by environment variables
···
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
SystemCallErrorNumber = "EPERM";
-
SystemCallFilter = ["@system-service"];
+
SystemCallFilter = [ "@system-service" ];
Type = "simple";
-
UMask = 0027;
+
UMask = 27;
};
-
restartTriggers = [settingsFileUnsubstituted];
+
restartTriggers = [ settingsFileUnsubstituted ];
};
};
}
+25 -29
modules/matrix/mautrix-messenger.nix
···
-
{
-
lib,
-
config,
-
pkgs,
-
...
-
}: let
+
{ lib, config, pkgs, ... }:
+
let
cfg = config.services.mautrix-messenger;
dataDir = "/var/lib/mautrix-messenger";
registrationFile = "${dataDir}/messenger-registration.yaml";
settingsFile = "${dataDir}/config.json";
-
settingsFileUnsubstituted = settingsFormat.generate "mautrix-messenger-config-unsubstituted.json" cfg.settings;
-
settingsFormat = pkgs.formats.json {};
+
settingsFileUnsubstituted =
+
settingsFormat.generate "mautrix-messenger-config-unsubstituted.json"
+
cfg.settings;
+
settingsFormat = pkgs.formats.json { };
appservicePort = 29320;
mkDefaults = lib.mapAttrsRecursive (n: v: lib.mkDefault v);
···
};
bridge = {
username_template = "messenger_{{.}}";
-
double_puppet_server_map = {};
-
login_shared_secret_map = {};
+
double_puppet_server_map = { };
+
login_shared_secret_map = { };
permissions."*" = "relay";
relay.enabled = true;
};
···
in {
options.services.mautrix-messenger = {
-
enable = lib.mkEnableOption (lib.mdDoc "mautrix-messenger, a puppeting/relaybot bridge between Matrix and Messenger.");
+
enable = lib.mkEnableOption (lib.mdDoc
+
"mautrix-messenger, a puppeting/relaybot bridge between Matrix and Messenger.");
settings = lib.mkOption {
type = settingsFormat.type;
···
ephemeral_events = false;
};
bridge = {
-
history_sync = {
-
request_full_sync = true;
-
};
+
history_sync = { request_full_sync = true; };
private_chat_portal_meta = true;
mute_bridging = true;
encryption = {
···
default = true;
require = true;
};
-
provisioning = {
-
shared_secret = "disable";
-
};
-
permissions = {
-
"example.com" = "user";
-
};
+
provisioning = { shared_secret = "disable"; };
+
permissions = { "example.com" = "user"; };
};
};
};
serviceDependencies = lib.mkOption {
type = with lib.types; listOf str;
-
default = lib.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit;
+
default = lib.optional config.services.matrix-synapse.enable
+
config.services.matrix-synapse.serviceUnit;
defaultText = lib.literalExpression ''
optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnits
'';
···
description = "Mautrix-Messenger bridge user";
};
-
users.groups.mautrix-messenger = {};
+
users.groups.mautrix-messenger = { };
services.mautrix-messenger.settings = lib.mkMerge (map mkDefaults [
defaultConfig
# Note: this is defined here to avoid the docs depending on `config`
-
{ homeserver.domain = config.services.matrix-synapse.settings.server_name; }
+
{
+
homeserver.domain = config.services.matrix-synapse.settings.server_name;
+
}
]);
systemd.services.mautrix-messenger = {
description = "Mautrix-Messenger Service - A Messenger bridge for Matrix";
-
wantedBy = ["multi-user.target"];
-
wants = ["network-online.target"] ++ cfg.serviceDependencies;
-
after = ["network-online.target"] ++ cfg.serviceDependencies;
+
wantedBy = [ "multi-user.target" ];
+
wants = [ "network-online.target" ] ++ cfg.serviceDependencies;
+
after = [ "network-online.target" ] ++ cfg.serviceDependencies;
preStart = ''
# substitute the settings file by environment variables
···
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
SystemCallErrorNumber = "EPERM";
-
SystemCallFilter = ["@system-service"];
+
SystemCallFilter = [ "@system-service" ];
Type = "simple";
-
UMask = 0027;
+
UMask = 27;
};
-
restartTriggers = [settingsFileUnsubstituted];
+
restartTriggers = [ settingsFileUnsubstituted ];
};
};
}
+26 -29
modules/matrix/mautrix-signal.nix
···
-
{
-
lib,
-
config,
-
pkgs,
-
...
-
}: let
+
{ lib, config, pkgs, ... }:
+
let
cfg = config.services.mautrix-signal;
dataDir = "/var/lib/mautrix-signal";
registrationFile = "${dataDir}/signal-registration.yaml";
settingsFile = "${dataDir}/config.json";
-
settingsFileUnsubstituted = settingsFormat.generate "mautrix-signal-config-unsubstituted.json" cfg.settings;
-
settingsFormat = pkgs.formats.json {};
+
settingsFileUnsubstituted =
+
settingsFormat.generate "mautrix-signal-config-unsubstituted.json"
+
cfg.settings;
+
settingsFormat = pkgs.formats.json { };
appservicePort = 29328;
mkDefaults = lib.mapAttrsRecursive (n: v: lib.mkDefault v);
···
};
bridge = {
username_template = "signal_{{.}}";
-
double_puppet_server_map = {};
-
login_shared_secret_map = {};
+
double_puppet_server_map = { };
+
login_shared_secret_map = { };
permissions."*" = "relay";
};
logging = {
···
in {
options.services.mautrix-signal = {
-
enable = lib.mkEnableOption (lib.mdDoc "mautrix-signal, a puppeting/relaybot bridge between Matrix and Signal.");
+
enable = lib.mkEnableOption (lib.mdDoc
+
"mautrix-signal, a puppeting/relaybot bridge between Matrix and Signal.");
settings = lib.mkOption {
type = settingsFormat.type;
···
ephemeral_events = false;
};
bridge = {
-
history_sync = {
-
request_full_sync = true;
-
};
+
history_sync = { request_full_sync = true; };
private_chat_portal_meta = true;
mute_bridging = true;
encryption = {
···
default = true;
require = true;
};
-
provisioning = {
-
shared_secret = "disable";
-
};
-
permissions = {
-
"example.com" = "user";
-
};
+
provisioning = { shared_secret = "disable"; };
+
permissions = { "example.com" = "user"; };
};
};
};
serviceDependencies = lib.mkOption {
type = with lib.types; listOf str;
-
default = lib.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit;
+
default = lib.optional config.services.matrix-synapse.enable
+
config.services.matrix-synapse.serviceUnit;
defaultText = lib.literalExpression ''
optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnits
'';
···
description = "Mautrix-Signal bridge user";
};
-
users.groups.mautrix-signal = {};
+
users.groups.mautrix-signal = { };
services.mautrix-signal.settings = lib.mkMerge (map mkDefaults [
defaultConfig
# Note: this is defined here to avoid the docs depending on `config`
-
{ homeserver.domain = config.services.matrix-synapse.settings.server_name; }
+
{
+
homeserver.domain = config.services.matrix-synapse.settings.server_name;
+
}
]);
systemd.services.mautrix-signal = {
···
# voice messages need `ffmpeg`
path = [ pkgs.ffmpeg ];
-
wantedBy = ["multi-user.target"];
-
wants = ["network-online.target"] ++ cfg.serviceDependencies;
-
after = ["network-online.target" "signald.service"] ++ cfg.serviceDependencies;
+
wantedBy = [ "multi-user.target" ];
+
wants = [ "network-online.target" ] ++ cfg.serviceDependencies;
+
after = [ "network-online.target" "signald.service" ]
+
++ cfg.serviceDependencies;
preStart = ''
# substitute the settings file by environment variables
···
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
SystemCallErrorNumber = "EPERM";
-
SystemCallFilter = ["@system-service"];
+
SystemCallFilter = [ "@system-service" ];
Type = "simple";
-
UMask = 0027;
+
UMask = 27;
};
-
restartTriggers = [settingsFileUnsubstituted];
+
restartTriggers = [ settingsFileUnsubstituted ];
};
};
}
+91 -82
modules/matrix/synapse.nix
···
let
cfg = config.eilean;
turnSharedSecretFile = "/run/matrix-synapse/turn-shared-secret";
-
in
-
{
+
in {
options.eilean.matrix = {
enable = mkEnableOption "matrix";
turn = mkOption {
···
enableACME = true;
forceSSL = true;
-
locations."= /.well-known/matrix/server".extraConfig =
-
let
-
# use 443 instead of the default 8448 port to unite
-
# the client-server and server-server port for simplicity
-
server = { "m.server" = "matrix.${config.networking.domain}:443"; };
-
in ''
-
default_type application/json;
-
return 200 '${builtins.toJSON server}';
-
'';
-
locations."= /.well-known/matrix/client".extraConfig =
-
let
-
client = {
-
"m.homeserver" = { "base_url" = "https://matrix.${config.networking.domain}"; };
-
"m.identity_server" = { "base_url" = "https://vector.im"; };
+
locations."= /.well-known/matrix/server".extraConfig = let
+
# use 443 instead of the default 8448 port to unite
+
# the client-server and server-server port for simplicity
+
server = { "m.server" = "matrix.${config.networking.domain}:443"; };
+
in ''
+
default_type application/json;
+
return 200 '${builtins.toJSON server}';
+
'';
+
locations."= /.well-known/matrix/client".extraConfig = let
+
client = {
+
"m.homeserver" = {
+
"base_url" = "https://matrix.${config.networking.domain}";
};
+
"m.identity_server" = { "base_url" = "https://vector.im"; };
+
};
# ACAO required to allow element-web on any URL to request this json file
# set other headers due to https://github.com/yandex/gixy/blob/master/docs/en/plugins/addheaderredefinition.md
-
in ''
-
default_type application/json;
-
add_header Access-Control-Allow-Origin *;
-
add_header Strict-Transport-Security max-age=31536000 always;
-
add_header X-Frame-Options SAMEORIGIN always;
-
add_header X-Content-Type-Options nosniff always;
-
add_header Content-Security-Policy "default-src 'self'; base-uri 'self'; frame-src 'self'; frame-ancestors 'self'; form-action 'self';" always;
-
add_header Referrer-Policy 'same-origin';
-
return 200 '${builtins.toJSON client}';
-
'';
+
in ''
+
default_type application/json;
+
add_header Access-Control-Allow-Origin *;
+
add_header Strict-Transport-Security max-age=31536000 always;
+
add_header X-Frame-Options SAMEORIGIN always;
+
add_header X-Content-Type-Options nosniff always;
+
add_header Content-Security-Policy "default-src 'self'; base-uri 'self'; frame-src 'self'; frame-ancestors 'self'; form-action 'self';" always;
+
add_header Referrer-Policy 'same-origin';
+
return 200 '${builtins.toJSON client}';
+
'';
};
# Reverse proxy for Matrix client-server and server-server communication
···
enable_registration = true;
registration_requires_token = true;
registration_shared_secret_path = cfg.matrix.registrationSecretFile;
-
listeners = [
-
{
-
port = 8008;
-
bind_addresses = [ "::1" "127.0.0.1" ];
-
type = "http";
-
tls = false;
-
x_forwarded = true;
-
resources = [
-
{
-
names = [ "client" "federation" ];
-
compress = false;
-
}
-
];
-
}
-
];
+
listeners = [{
+
port = 8008;
+
bind_addresses = [ "::1" "127.0.0.1" ];
+
type = "http";
+
tls = false;
+
x_forwarded = true;
+
resources = [{
+
names = [ "client" "federation" ];
+
compress = false;
+
}];
+
}];
max_upload_size = "100M";
-
app_service_config_files =
-
(optional cfg.matrix.bridges.whatsapp "/var/lib/mautrix-whatsapp/whatsapp-registration.yaml") ++
-
(optional cfg.matrix.bridges.signal "/var/lib/mautrix-signal/signal-registration.yaml") ++
-
(optional cfg.matrix.bridges.instagram "/var/lib/mautrix-instagram/instagram-registration.yaml") ++
-
(optional cfg.matrix.bridges.messenger "/var/lib/mautrix-messenger/messenger-registration.yaml");
+
app_service_config_files = (optional cfg.matrix.bridges.whatsapp
+
"/var/lib/mautrix-whatsapp/whatsapp-registration.yaml")
+
++ (optional cfg.matrix.bridges.signal
+
"/var/lib/mautrix-signal/signal-registration.yaml")
+
++ (optional cfg.matrix.bridges.instagram
+
"/var/lib/mautrix-instagram/instagram-registration.yaml")
+
++ (optional cfg.matrix.bridges.messenger
+
"/var/lib/mautrix-messenger/messenger-registration.yaml");
}
(mkIf cfg.matrix.turn {
turn_uris = with config.services.coturn; [
···
turn_user_lifetime = "1h";
})
];
-
extraConfigFiles = mkIf cfg.matrix.turn (
-
[ turnSharedSecretFile ]
-
);
+
extraConfigFiles = mkIf cfg.matrix.turn ([ turnSharedSecretFile ]);
};
-
systemd.services.matrix-synapse-turn-shared-secret-generator = mkIf cfg.matrix.turn {
-
description = "Generate matrix synapse turn shared secret config file";
-
script = ''
-
mkdir -p "$(dirname '${turnSharedSecretFile}')"
-
echo "turn_shared_secret: $(cat '${config.services.coturn.static-auth-secret-file}')" > '${turnSharedSecretFile}'
-
chmod 770 '${turnSharedSecretFile}'
-
chown ${config.systemd.services.matrix-synapse.serviceConfig.User}:${config.systemd.services.matrix-synapse.serviceConfig.Group} '${turnSharedSecretFile}'
+
systemd.services.matrix-synapse-turn-shared-secret-generator =
+
mkIf cfg.matrix.turn {
+
description = "Generate matrix synapse turn shared secret config file";
+
script = ''
+
mkdir -p "$(dirname '${turnSharedSecretFile}')"
+
echo "turn_shared_secret: $(cat '${config.services.coturn.static-auth-secret-file}')" > '${turnSharedSecretFile}'
+
chmod 770 '${turnSharedSecretFile}'
+
chown ${config.systemd.services.matrix-synapse.serviceConfig.User}:${config.systemd.services.matrix-synapse.serviceConfig.Group} '${turnSharedSecretFile}'
'';
-
serviceConfig.Type = "oneshot";
-
serviceConfig.RemainAfterExit = true;
-
after = [ "coturn-static-auth-secret-generator.service" ];
-
requires = [ "coturn-static-auth-secret-generator.service" ];
-
};
-
systemd.services."matrix-synapse".after = mkIf cfg.matrix.turn [ "matrix-synapse-turn-shared-secret-generator.service" ];
-
systemd.services."matrix-synapse".requires = mkIf cfg.matrix.turn [ "matrix-synapse-turn-shared-secret-generator.service" ];
+
serviceConfig.Type = "oneshot";
+
serviceConfig.RemainAfterExit = true;
+
after = [ "coturn-static-auth-secret-generator.service" ];
+
requires = [ "coturn-static-auth-secret-generator.service" ];
+
};
+
systemd.services."matrix-synapse".after = mkIf cfg.matrix.turn
+
[ "matrix-synapse-turn-shared-secret-generator.service" ];
+
systemd.services."matrix-synapse".requires = mkIf cfg.matrix.turn
+
[ "matrix-synapse-turn-shared-secret-generator.service" ];
systemd.services.matrix-synapse.serviceConfig.SupplementaryGroups =
-
(optional cfg.matrix.bridges.whatsapp config.systemd.services.mautrix-whatsapp.serviceConfig.Group) ++
-
(optional cfg.matrix.bridges.signal config.systemd.services.mautrix-signal.serviceConfig.Group) ++
-
(optional cfg.matrix.bridges.instagram config.systemd.services.mautrix-instagram.serviceConfig.Group) ++
-
(optional cfg.matrix.bridges.messenger config.systemd.services.mautrix-messenger.serviceConfig.Group);
+
(optional cfg.matrix.bridges.whatsapp
+
config.systemd.services.mautrix-whatsapp.serviceConfig.Group)
+
++ (optional cfg.matrix.bridges.signal
+
config.systemd.services.mautrix-signal.serviceConfig.Group)
+
++ (optional cfg.matrix.bridges.instagram
+
config.systemd.services.mautrix-instagram.serviceConfig.Group)
+
++ (optional cfg.matrix.bridges.messenger
+
config.systemd.services.mautrix-messenger.serviceConfig.Group);
services.mautrix-whatsapp = mkIf cfg.matrix.bridges.whatsapp {
enable = true;
-
settings.homeserver.address = "https://matrix.${config.networking.domain}";
+
settings.homeserver.address =
+
"https://matrix.${config.networking.domain}";
settings.homeserver.domain = config.networking.domain;
settings.appservice.hostname = "localhost";
settings.appservice.address = "http://localhost:29318";
settings.bridge.personal_filtering_spaces = true;
settings.bridge.history_sync.backfill = false;
-
settings.bridge.permissions."@${config.eilean.username}:${config.networking.domain}" = "admin";
+
settings.bridge.permissions."@${config.eilean.username}:${config.networking.domain}" =
+
"admin";
};
services.mautrix-signal = mkIf cfg.matrix.bridges.signal {
enable = true;
-
settings.homeserver.address = "https://matrix.${config.networking.domain}";
+
settings.homeserver.address =
+
"https://matrix.${config.networking.domain}";
settings.homeserver.domain = config.networking.domain;
settings.appservice.hostname = "localhost";
settings.appservice.address = "http://localhost:29328";
settings.bridge.personal_filtering_spaces = true;
-
settings.bridge.permissions."@${config.eilean.username}:${config.networking.domain}" = "admin";
+
settings.bridge.permissions."@${config.eilean.username}:${config.networking.domain}" =
+
"admin";
};
services.mautrix-instagram = mkIf cfg.matrix.bridges.instagram {
enable = true;
-
settings.homeserver.address = "https://matrix.${config.networking.domain}";
+
settings.homeserver.address =
+
"https://matrix.${config.networking.domain}";
settings.homeserver.domain = config.networking.domain;
settings.appservice.hostname = "localhost";
settings.appservice.address = "http://localhost:29319";
settings.bridge.personal_filtering_spaces = true;
settings.bridge.backfill.enabled = false;
-
settings.bridge.permissions."@${config.eilean.username}:${config.networking.domain}" = "admin";
+
settings.bridge.permissions."@${config.eilean.username}:${config.networking.domain}" =
+
"admin";
};
services.mautrix-messenger = mkIf cfg.matrix.bridges.messenger {
enable = true;
-
settings.homeserver.address = "https://matrix.${config.networking.domain}";
+
settings.homeserver.address =
+
"https://matrix.${config.networking.domain}";
settings.homeserver.domain = config.networking.domain;
settings.appservice.hostname = "localhost";
settings.appservice.address = "http://localhost:29320";
settings.bridge.personal_filtering_spaces = true;
settings.bridge.backfill.enabled = false;
-
settings.bridge.permissions."@${config.eilean.username}:${config.networking.domain}" = "admin";
+
settings.bridge.permissions."@${config.eilean.username}:${config.networking.domain}" =
+
"admin";
};
eilean.turn.enable = mkIf cfg.matrix.turn true;
eilean.dns.enable = true;
-
eilean.services.dns.zones.${config.networking.domain}.records = [
-
{
-
name = "matrix";
-
type = "CNAME";
-
data = "vps";
-
}
-
];
+
eilean.services.dns.zones.${config.networking.domain}.records = [{
+
name = "matrix";
+
type = "CNAME";
+
data = "vps";
+
}];
};
}
+19 -20
modules/services/dns/bind.nix
···
{ pkgs, config, lib, ... }:
-
let cfg = config.eilean.services.dns; in
-
lib.mkIf (cfg.enable && cfg.server == "bind") {
+
let cfg = config.eilean.services.dns;
+
in lib.mkIf (cfg.enable && cfg.server == "bind") {
services.bind = {
enable = true;
# recursive resolver
# cacheNetworks = [ "0.0.0.0/0" ];
-
zones =
-
let mapZones = zonename: zone:
-
{
-
master = true;
-
file = "${config.services.bind.directory}/${zonename}";
-
#file = "${import ./zonefile.nix { inherit pkgs config lib zonename zone; }}/${zonename}";
-
# axfr zone transfer
-
slaves = [
-
"127.0.0.1"
-
];
-
};
-
in builtins.mapAttrs mapZones cfg.zones;
+
zones = let
+
mapZones = zonename: zone: {
+
master = true;
+
file = "${config.services.bind.directory}/${zonename}";
+
#file = "${import ./zonefile.nix { inherit pkgs config lib zonename zone; }}/${zonename}";
+
# axfr zone transfer
+
slaves = [ "127.0.0.1" ];
+
};
+
in builtins.mapAttrs mapZones cfg.zones;
};
### bind prestart copy zonefiles
-
systemd.services.bind.preStart =
-
let ops =
-
let mapZones = zonename: zone:
+
systemd.services.bind.preStart = let
+
ops = let
+
mapZones = zonename: zone:
let
-
zonefile = "${import ./zonefile.nix { inherit pkgs config lib zonename zone; }}/${zonename}";
+
zonefile = "${
+
import ./zonefile.nix { inherit pkgs config lib zonename zone; }
+
}/${zonename}";
path = "${config.services.bind.directory}/${zonename}";
in ''
if ! diff ${zonefile} ${path} > /dev/null; then
···
rm -f ${path}.signed.jnl
fi
'';
-
in lib.attrsets.mapAttrsToList mapZones cfg.zones;
-
in builtins.concatStringsSep "\n" ops;
+
in lib.attrsets.mapAttrsToList mapZones cfg.zones;
+
in builtins.concatStringsSep "\n" ops;
}
+12 -23
modules/services/dns/default.nix
···
default = "dns";
};
# TODO auto increment
-
serial = mkOption {
-
type = types.int;
-
};
+
serial = mkOption { type = types.int; };
refresh = mkOption {
type = types.int;
default = 3600; # 1hr
···
default = 3600; # 1hr
};
};
-
records =
-
let recordOpts.options = {
-
name = mkOption {
-
type = types.str;
-
};
+
records = let
+
recordOpts.options = {
+
name = mkOption { type = types.str; };
ttl = mkOption {
type = with types; nullOr int;
default = null;
};
-
type = mkOption {
-
type = types.str;
-
};
-
data = mkOption {
-
type = types.str;
-
};
+
type = mkOption { type = types.str; };
+
data = mkOption { type = types.str; };
};
-
in mkOption {
-
type = with types; listOf (submodule recordOpts);
-
default = [ ];
-
};
+
in mkOption {
+
type = with types; listOf (submodule recordOpts);
+
default = [ ];
+
};
};
-
in
-
{
+
in {
imports = [ ./bind.nix ];
options.eilean.services.dns = {
···
type = types.bool;
default = true;
};
-
zones = mkOption {
-
type = with types; attrsOf (submodule zoneOptions);
-
};
+
zones = mkOption { type = with types; attrsOf (submodule zoneOptions); };
};
config.networking.firewall = mkIf config.eilean.services.dns.openFirewall {
+4 -12
modules/services/dns/zonefile.nix
···
-
{
-
pkgs,
-
config,
-
lib,
-
zonename,
-
zone,
-
...
-
}:
+
{ pkgs, config, lib, zonename, zone, ... }:
pkgs.writeTextFile {
name = "zonefile-${zonename}";
···
${builtins.toString zone.soa.expire}
${builtins.toString zone.soa.negativeCacheTtl}
)
-
${
-
lib.strings.concatStringsSep "\n"
-
(builtins.map (rr: "${rr.name} IN ${builtins.toString rr.ttl} ${rr.type} ${rr.data}") zone.records)
-
}
+
${lib.strings.concatStringsSep "\n" (builtins.map
+
(rr: "${rr.name} IN ${builtins.toString rr.ttl} ${rr.type} ${rr.data}")
+
zone.records)}
'';
}
+12 -20
modules/turn.nix
···
cfg = config.eilean;
domain = config.networking.domain;
staticAuthSecretFile = "/run/coturn/static-auth-secret";
-
in
-
{
-
options.eilean.turn = {
-
enable = mkEnableOption "TURN server";
-
};
+
in {
+
options.eilean.turn = { enable = mkEnableOption "TURN server"; };
config = mkIf cfg.turn.enable {
services.coturn = rec {
···
use-auth-secret = true;
static-auth-secret-file = staticAuthSecretFile;
realm = "turn.${domain}";
-
relay-ips = with config.eilean; [
-
serverIpv4
-
serverIpv6
-
];
+
relay-ips = with config.eilean; [ serverIpv4 serverIpv6 ];
cert = "${config.security.acme.certs.${realm}.directory}/full.pem";
pkey = "${config.security.acme.certs.${realm}.directory}/key.pem";
};
···
};
};
-
networking.firewall =
-
with config.services.coturn;
+
networking.firewall = with config.services.coturn;
let
turn-range = {
from = min-port;
···
allowedTCPPortRanges = [ turn-range ];
allowedUDPPorts = stun-ports;
allowedUDPPortRanges = [ turn-range ];
-
};
+
};
security.acme.certs.${config.services.coturn.realm} = {
-
postRun = "systemctl reload nginx.service; systemctl restart coturn.service";
+
postRun =
+
"systemctl reload nginx.service; systemctl restart coturn.service";
group = "turnserver";
};
services.nginx.enable = true;
···
users.groups."turnserver".members = [ config.services.nginx.user ];
eilean.dns.enable = true;
-
eilean.services.dns.zones.${config.networking.domain}.records = [
-
{
-
name = "turn";
-
type = "CNAME";
-
data = "vps";
-
}
-
];
+
eilean.services.dns.zones.${config.networking.domain}.records = [{
+
name = "turn";
+
type = "CNAME";
+
data = "vps";
+
}];
};
}
+40 -52
modules/wireguard/default.nix
···
{ pkgs, config, lib, ... }:
with lib;
-
let cfg = config.wireguard; in
-
{
+
let cfg = config.wireguard;
+
in {
options.wireguard = {
enable = mkEnableOption "wireguard";
server = mkOption {
type = with types; bool;
-
default =
-
if cfg.hosts ? config.networking.hostName then
-
cfg.hosts.${config.networking.hostName}.server
-
else false;
+
default = if cfg.hosts ? config.networking.hostName then
+
cfg.hosts.${config.networking.hostName}.server
+
else
+
false;
};
-
hosts =
-
let hostOps = { ... }: {
+
hosts = let
+
hostOps = { ... }: {
options = {
-
ip = mkOption {
-
type = types.str;
-
};
-
publicKey = mkOption {
-
type = types.str;
-
};
+
ip = mkOption { type = types.str; };
+
publicKey = mkOption { type = types.str; };
server = mkOption {
type = types.bool;
default = false;
···
};
};
};
-
in mkOption {
-
type = with types; attrsOf (submodule hostOps);
-
default = {};
-
};
+
in mkOption {
+
type = with types; attrsOf (submodule hostOps);
+
default = { };
+
};
};
config = mkIf cfg.enable {
···
networking = mkMerge [
{
# populate /etc/hosts with hostnames and IPs
-
extraHosts = builtins.concatStringsSep "\n" (
-
attrsets.mapAttrsToList (
-
hostName: values: "${values.ip} ${hostName}"
-
) cfg.hosts
-
);
+
extraHosts = builtins.concatStringsSep "\n" (attrsets.mapAttrsToList
+
(hostName: values: "${values.ip} ${hostName}") cfg.hosts);
firewall = {
allowedUDPPorts = [ 51820 ];
···
wireguard = {
enable = true;
-
interfaces.wg0 = let hostName = config.networking.hostName; in {
-
ips =
-
if cfg.hosts ? hostname then
-
[ "${cfg.hosts."${hostName}".ip}/24" ]
-
else [ ];
-
listenPort = 51820;
+
interfaces.wg0 = let hostName = config.networking.hostName;
+
in {
+
ips = if cfg.hosts ? hostname then
+
[ "${cfg.hosts."${hostName}".ip}/24" ]
+
else
+
[ ];
+
listenPort = 51820;
privateKeyFile = cfg.hosts."${hostName}".privateKeyFile;
-
peers =
-
let
-
serverPeers = attrsets.mapAttrsToList
-
(hostName: values:
-
if values.server then
-
{
-
allowedIPs = [ "10.0.0.0/24" ];
-
publicKey = values.publicKey;
-
endpoint = "${values.endpoint}:51820";
-
persistentKeepalive = values.persistentKeepalive;
-
}
-
else {})
-
cfg.hosts;
-
# remove empty elements
-
cleanedServerPeers = lists.remove { } serverPeers;
-
in mkIf (!cfg.server) cleanedServerPeers;
+
peers = let
+
serverPeers = attrsets.mapAttrsToList (hostName: values:
+
if values.server then {
+
allowedIPs = [ "10.0.0.0/24" ];
+
publicKey = values.publicKey;
+
endpoint = "${values.endpoint}:51820";
+
persistentKeepalive = values.persistentKeepalive;
+
} else
+
{ }) cfg.hosts;
+
# remove empty elements
+
cleanedServerPeers = lists.remove { } serverPeers;
+
in mkIf (!cfg.server) cleanedServerPeers;
};
};
}
···
# add clients
peers = with lib.attrsets;
-
mapAttrsToList (
-
hostName: values: {
-
allowedIPs = [ "${values.ip}/32" ];
-
publicKey = values.publicKey;
-
persistentKeepalive = values.persistentKeepalive;
-
}
-
) cfg.hosts;
+
mapAttrsToList (hostName: values: {
+
allowedIPs = [ "${values.ip}/32" ];
+
publicKey = values.publicKey;
+
persistentKeepalive = values.persistentKeepalive;
+
}) cfg.hosts;
};
})
];
+2 -1
pkgs/mautrix-meta.nix
···
meta = with lib; {
homepage = "https://github.com/mautrix/meta";
-
description = " A Matrix-Facebook Messenger and Instagram DM puppeting bridge.";
+
description =
+
" A Matrix-Facebook Messenger and Instagram DM puppeting bridge.";
license = licenses.agpl3Plus;
mainProgram = "mautrix-meta";
};
+2 -4
template/configuration.nix
···
{ pkgs, config, lib, ... }:
{
-
imports = [
-
./hardware-configuration.nix
-
];
+
imports = [ ./hardware-configuration.nix ];
boot.loader = {
systemd-boot.enable = true;
···
# Before changing this value read the documentation for this option
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
system.stateVersion = "23.05"; # Did you read the comment?
-
}
+
}
+17 -16
template/flake.nix
···
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
-
eilean.url ="github:RyanGibb/eilean-nix/main";
+
eilean.url = "github:RyanGibb/eilean-nix/main";
# replace the below line to manage the Nixpkgs instance yourself
nixpkgs.follows = "eilean/nixpkgs";
#eilean.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { self, nixpkgs, eilean, ... }@inputs:
-
let hostname = "eilean"; in
-
rec {
-
nixosConfigurations.${hostname} = nixpkgs.lib.nixosSystem {
+
let hostname = "eilean";
+
in rec {
+
nixosConfigurations.${hostname} = nixpkgs.lib.nixosSystem {
system = null;
pkgs = null;
modules = [
-
./configuration.nix
-
eilean.nixosModules.default
-
{
-
networking.hostName = hostname;
-
# pin nix command's nixpkgs flake to the system flake to avoid unnecessary downloads
-
nix.registry.nixpkgs.flake = nixpkgs;
-
# record git revision (can be queried with `nixos-version --json)
-
system.configurationRevision = nixpkgs.lib.mkIf (self ? rev) self.rev;
-
}
-
];
-
};
+
./configuration.nix
+
eilean.nixosModules.default
+
{
+
networking.hostName = hostname;
+
# pin nix command's nixpkgs flake to the system flake to avoid unnecessary downloads
+
nix.registry.nixpkgs.flake = nixpkgs;
+
# record git revision (can be queried with `nixos-version --json)
+
system.configurationRevision =
+
nixpkgs.lib.mkIf (self ? rev) self.rev;
+
}
+
];
};
-
}
+
};
+
}