feat(copyparty): add NixOS guest
This commit was merged in pull request #46.
This commit is contained in:
@@ -18,6 +18,11 @@ The domain model (Host, Module, Skeleton, Auto-loader, Enable convention, overla
|
|||||||
|
|
||||||
## Gotchas
|
## Gotchas
|
||||||
|
|
||||||
|
- Guest modules are host-agnostic wiring, while host files own deployment-specific service configuration such as served volumes and host paths.
|
||||||
|
For example, `guests/copyparty.nix` should expose options and translate them into guest mounts/service settings, while `hosts/pikachu/default.nix` chooses `/inbox`, `/media`, and their backing paths.
|
||||||
|
- `sops.secrets.<name>.owner` must name a user known on the decrypting host.
|
||||||
|
For guest-only service users, do not set the owner to the guest uid unless the host also declares that user.
|
||||||
|
Use a host-visible owner plus a shared group mode when an identity-mapped guest service can read through its group.
|
||||||
- Actual Budget's NixOS service runs `actual-server` with `WorkingDirectory = services.actual.settings.dataDir`, and the `actual-server` CLI wrapper sets `ACTUAL_CONFIG_PATH=./config.json` when that current directory contains `config.json`, overriding the NixOS module's generated config path before the server loads configuration.
|
- Actual Budget's NixOS service runs `actual-server` with `WorkingDirectory = services.actual.settings.dataDir`, and the `actual-server` CLI wrapper sets `ACTUAL_CONFIG_PATH=./config.json` when that current directory contains `config.json`, overriding the NixOS module's generated config path before the server loads configuration.
|
||||||
When migrating old community-script state to the normal upstream `/data` shape, move or rewrite a copied `/data/config.json` so it cannot shadow the declarative module settings.
|
When migrating old community-script state to the normal upstream `/data` shape, move or rewrite a copied `/data/config.json` so it cannot shadow the declarative module settings.
|
||||||
- Subagent completion delivery is non-blocking through immediate spawn, milestone notifications, retained terminal entries, and `subagent_list` or `subagent_result` retrieval.
|
- Subagent completion delivery is non-blocking through immediate spawn, milestone notifications, retained terminal entries, and `subagent_list` or `subagent_result` retrieval.
|
||||||
@@ -78,10 +83,10 @@ The domain model (Host, Module, Skeleton, Auto-loader, Enable convention, overla
|
|||||||
- `home.sessionVariables` do **not** reach the Hyprland session, since UWSM does not source `hm-session-vars.sh`.
|
- `home.sessionVariables` do **not** reach the Hyprland session, since UWSM does not source `hm-session-vars.sh`.
|
||||||
The cursor is therefore set through Hyprland's own `env = KEY,VALUE` in `modules/desktop/hyprland/hyprland.nix`, sourced from `config.stylix.cursor`.
|
The cursor is therefore set through Hyprland's own `env = KEY,VALUE` in `modules/desktop/hyprland/hyprland.nix`, sourced from `config.stylix.cursor`.
|
||||||
Bibata ships XCursor format only (no `hyprcursor/` dir), rendered through Hyprland's XCursor fallback, so `XCURSOR_*` and `HYPRCURSOR_*` naming the same theme are both safe.
|
Bibata ships XCursor format only (no `hyprcursor/` dir), rendered through Hyprland's XCursor fallback, so `XCURSOR_*` and `HYPRCURSOR_*` naming the same theme are both safe.
|
||||||
- `neogaia`, the repo's only host, is a wifi laptop with a btrfs root and no ZFS pools, so it cannot honestly carry `modules.network`, `modules.zfs`, or a networked/pool-mounted guest.
|
- `neogaia` is a wifi laptop with a btrfs root and no ZFS pools, so it cannot honestly carry `modules.network`, `modules.zfs`, or a networked/pool-mounted guest.
|
||||||
Enabling networkd takes over its DNS, its CachyOS `zfs-kernel` build is marked broken, and it has no bridge or pool to attach to.
|
Enabling networkd takes over its DNS, its CachyOS `zfs-kernel` build is marked broken, and it has no bridge or pool to attach to.
|
||||||
Verify these against it ad hoc through `nixosConfigurations.neogaia.extendModules` (forcing a ZFS-capable `boot.kernelPackages` for the zfs case) plus `nix eval` of the derived values, never by committing the enablement.
|
Verify these against it ad hoc through `nixosConfigurations.neogaia.extendModules` (forcing a ZFS-capable `boot.kernelPackages` for the zfs case) plus `nix eval` of the derived values, never by committing the enablement.
|
||||||
A committed guest therefore leaves `vlan`, `mounts`, and `secrets` unset, and the standing enablement waits for the first wired server host with real storage.
|
Committed networked and pool-mounted guests belong on a wired storage host such as `pikachu`, not on `neogaia`.
|
||||||
- Herdr key names for shifted punctuation are not interchangeable with the physical base key plus `shift`.
|
- Herdr key names for shifted punctuation are not interchangeable with the physical base key plus `shift`.
|
||||||
The tab rename binding must use the produced literal, such as `prefix+<`, rather than `prefix+shift+comma`.
|
The tab rename binding must use the produced literal, such as `prefix+<`, rather than `prefix+shift+comma`.
|
||||||
- Flake-managed Pi extension, prompt, and skill directories may still be written directly for throwaway development or local experiments.
|
- Flake-managed Pi extension, prompt, and skill directories may still be written directly for throwaway development or local experiments.
|
||||||
|
|||||||
126
guests/copyparty.nix
Normal file
126
guests/copyparty.nix
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
my,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
cfg = config.guests.copyparty;
|
||||||
|
serviceUid = my.serviceUid "copyparty";
|
||||||
|
|
||||||
|
volumeType = lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
hostPath = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "Host path bind-mounted into the Copyparty guest for this volume.";
|
||||||
|
};
|
||||||
|
|
||||||
|
path = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "Guest path served by this Copyparty volume.";
|
||||||
|
};
|
||||||
|
|
||||||
|
access = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf (lib.types.listOf lib.types.str);
|
||||||
|
default = { };
|
||||||
|
description = "Copyparty access map from permission string to account names.";
|
||||||
|
};
|
||||||
|
|
||||||
|
flags = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
default = [ ];
|
||||||
|
description = "Copyparty volume flags.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
(my.guest {
|
||||||
|
name = "copyparty";
|
||||||
|
interior = {
|
||||||
|
modules.services.copyparty = {
|
||||||
|
enable = true;
|
||||||
|
inherit (cfg) accountName stateDir;
|
||||||
|
passwordFile = "/run/secrets/${cfg.passwordSecret}";
|
||||||
|
volumes = lib.mapAttrs (_urlPath: volume: {
|
||||||
|
inherit (volume) path access flags;
|
||||||
|
}) cfg.volumes;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
options.guests.copyparty = {
|
||||||
|
accountName = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "alexion";
|
||||||
|
description = "Copyparty account name configured for password authentication.";
|
||||||
|
};
|
||||||
|
|
||||||
|
passwordSecret = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "services/copyparty/users/alexion/password";
|
||||||
|
description = "SOPS secret name containing the Copyparty account password.";
|
||||||
|
};
|
||||||
|
|
||||||
|
stateDir = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "/var/lib/copyparty";
|
||||||
|
description = "Guest path where Copyparty stores service state.";
|
||||||
|
};
|
||||||
|
|
||||||
|
statePath = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
example = "/srv/copyparty";
|
||||||
|
description = "Host path bind-mounted as Copyparty's state directory.";
|
||||||
|
};
|
||||||
|
|
||||||
|
volumes = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf volumeType;
|
||||||
|
default = { };
|
||||||
|
description = "Copyparty volumes keyed by their URL path.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = cfg.statePath != null;
|
||||||
|
message = "guests.copyparty.statePath must point at the host directory that stores Copyparty state.";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = cfg.volumes != { };
|
||||||
|
message = "guests.copyparty.volumes must declare the host's served Copyparty volumes.";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
guests.copyparty = {
|
||||||
|
secrets = [ cfg.passwordSecret ];
|
||||||
|
mounts =
|
||||||
|
{
|
||||||
|
${cfg.stateDir} = {
|
||||||
|
hostPath = cfg.statePath;
|
||||||
|
create = {
|
||||||
|
owner = serviceUid;
|
||||||
|
group = "storage";
|
||||||
|
mode = "0770";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// lib.mapAttrs' (_urlPath: volume: lib.nameValuePair volume.path { inherit (volume) hostPath; }) cfg.volumes;
|
||||||
|
limits = {
|
||||||
|
memory = lib.mkDefault "2G";
|
||||||
|
cpu = lib.mkDefault "200%";
|
||||||
|
tasksMax = lib.mkDefault 512;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
sops.secrets.${cfg.passwordSecret} = {
|
||||||
|
owner = "root";
|
||||||
|
group = "storage";
|
||||||
|
mode = "0440";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -81,6 +81,35 @@
|
|||||||
dataPath = "/pikachu/data/actualbudget";
|
dataPath = "/pikachu/data/actualbudget";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
guests.copyparty = {
|
||||||
|
enable = true;
|
||||||
|
vlan = 20;
|
||||||
|
mac = "02:23:20:39:23:01";
|
||||||
|
statePath = "/pikachu/data/copyparty";
|
||||||
|
volumes = {
|
||||||
|
"/inbox" = {
|
||||||
|
hostPath = "/pikachu/__inbox";
|
||||||
|
path = "/srv/inbox";
|
||||||
|
access.A = [ "alexion" ];
|
||||||
|
flags = [
|
||||||
|
"d2d"
|
||||||
|
"d2t"
|
||||||
|
"dthumb"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
"/media" = {
|
||||||
|
hostPath = "/pikachu/media";
|
||||||
|
path = "/srv/media";
|
||||||
|
access.A = [ "alexion" ];
|
||||||
|
flags = [
|
||||||
|
"d2d"
|
||||||
|
"d2t"
|
||||||
|
"dthumb"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
pciutils
|
pciutils
|
||||||
smartmontools
|
smartmontools
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ let
|
|||||||
genAttrs
|
genAttrs
|
||||||
flatten
|
flatten
|
||||||
hasSuffix
|
hasSuffix
|
||||||
|
escapeShellArg
|
||||||
mapAttrsToList
|
mapAttrsToList
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -133,6 +134,19 @@ let
|
|||||||
TasksMax = cfg.limits.tasksMax;
|
TasksMax = cfg.limits.tasksMax;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
createdMounts = lib.filterAttrs (_guestPath: m: m.create != null) cfg.mounts;
|
||||||
|
mountCreateScript = lib.concatStringsSep "\n" (
|
||||||
|
mapAttrsToList (
|
||||||
|
_guestPath: m:
|
||||||
|
let
|
||||||
|
create = m.create;
|
||||||
|
in
|
||||||
|
''
|
||||||
|
${config.system.path}/bin/install -d -o ${escapeShellArg (toString create.owner)} -g ${escapeShellArg create.group} -m ${escapeShellArg create.mode} ${escapeShellArg m.hostPath}
|
||||||
|
''
|
||||||
|
) createdMounts
|
||||||
|
);
|
||||||
|
|
||||||
# A networked guest owns its bridged interface through its own networkd, the only stable MAC pin for a nested container.
|
# A networked guest owns its bridged interface through its own networkd, the only stable MAC pin for a nested container.
|
||||||
# The interface is eth0, the name a nested container gives its bridged veth.
|
# The interface is eth0, the name a nested container gives its bridged veth.
|
||||||
# It takes the placement MAC, and the static address or DHCP when that is unset.
|
# It takes the placement MAC, and the static address or DHCP when that is unset.
|
||||||
@@ -219,6 +233,39 @@ let
|
|||||||
service must write to the pool data it owns.
|
service must write to the pool data it owns.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
create = lib.mkOption {
|
||||||
|
type = lib.types.nullOr (lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
type = lib.mkOption {
|
||||||
|
type = lib.types.enum [ "directory" ];
|
||||||
|
default = "directory";
|
||||||
|
description = "Host path type to create before the guest starts.";
|
||||||
|
};
|
||||||
|
owner = lib.mkOption {
|
||||||
|
type = lib.types.oneOf [
|
||||||
|
lib.types.str
|
||||||
|
lib.types.ints.positive
|
||||||
|
];
|
||||||
|
description = "Owner assigned to the created host path.";
|
||||||
|
};
|
||||||
|
group = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "Group assigned to the created host path.";
|
||||||
|
};
|
||||||
|
mode = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
example = "0770";
|
||||||
|
description = "Mode assigned to the created host path.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
Opt-in host path creation before the guest starts. Existing
|
||||||
|
paths are left enabled unless this is set, so served storage
|
||||||
|
roots can still be required to exist explicitly.
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -341,7 +388,10 @@ let
|
|||||||
# is up but not after that specific bridge existing.
|
# is up but not after that specific bridge existing.
|
||||||
systemd.services."container@${machineName}" = lib.mkIf (cfg.backend == "container") (
|
systemd.services."container@${machineName}" = lib.mkIf (cfg.backend == "container") (
|
||||||
lib.mkMerge [
|
lib.mkMerge [
|
||||||
{ serviceConfig = limitConfig; }
|
{
|
||||||
|
serviceConfig = limitConfig;
|
||||||
|
preStart = lib.mkIf (createdMounts != { }) mountCreateScript;
|
||||||
|
}
|
||||||
(lib.mkIf networked (
|
(lib.mkIf networked (
|
||||||
let
|
let
|
||||||
bridgeDevice = "sys-subsystem-net-devices-${lib.replaceStrings [ "-" ] [ "\\x2d" ] (bridgeName cfg.vlan)}.device";
|
bridgeDevice = "sys-subsystem-net-devices-${lib.replaceStrings [ "-" ] [ "\\x2d" ] (bridgeName cfg.vlan)}.device";
|
||||||
|
|||||||
179
modules/services/copyparty.nix
Normal file
179
modules/services/copyparty.nix
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
my,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
cfg = config.modules.services.copyparty;
|
||||||
|
|
||||||
|
volumeType = lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
path = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "Filesystem path served by this volume.";
|
||||||
|
};
|
||||||
|
|
||||||
|
access = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf (lib.types.listOf lib.types.str);
|
||||||
|
default = { };
|
||||||
|
example = {
|
||||||
|
A = [ "alexion" ];
|
||||||
|
};
|
||||||
|
description = "Copyparty access map from permission string to account names.";
|
||||||
|
};
|
||||||
|
|
||||||
|
flags = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
default = [ ];
|
||||||
|
example = [ "dthumb" ];
|
||||||
|
description = "Copyparty volume flags.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
renderAccess = permission: accounts: " ${permission}: ${lib.concatStringsSep ", " accounts}";
|
||||||
|
|
||||||
|
renderFlags = flags: lib.concatMapStringsSep "\n" (flag: " ${flag}") flags;
|
||||||
|
|
||||||
|
renderVolume = urlPath: volume: ''
|
||||||
|
[${urlPath}]
|
||||||
|
${volume.path}
|
||||||
|
accs:
|
||||||
|
${lib.concatStringsSep "\n" (lib.mapAttrsToList renderAccess volume.access)}
|
||||||
|
${lib.optionalString (volume.flags != [ ]) ''
|
||||||
|
flags:
|
||||||
|
${renderFlags volume.flags}
|
||||||
|
''}
|
||||||
|
'';
|
||||||
|
|
||||||
|
renderedVolumes = lib.concatStringsSep "\n" (lib.mapAttrsToList renderVolume cfg.volumes);
|
||||||
|
|
||||||
|
configScript = pkgs.writeShellScript "copyparty-config" ''
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
umask 077
|
||||||
|
password="$(${pkgs.coreutils}/bin/tr -d '\n' < "$CREDENTIALS_DIRECTORY/password")"
|
||||||
|
|
||||||
|
cat > /run/copyparty/copyparty.conf <<EOF
|
||||||
|
[global]
|
||||||
|
p: ${toString cfg.port}
|
||||||
|
usernames
|
||||||
|
http-only
|
||||||
|
no-crt
|
||||||
|
no-thumb
|
||||||
|
no-snap
|
||||||
|
no-rescan
|
||||||
|
ses-db: ${cfg.stateDir}/sessions.db
|
||||||
|
chpw-db: ${cfg.stateDir}/chpw.json
|
||||||
|
shr-db: ${cfg.stateDir}/shares.db
|
||||||
|
|
||||||
|
[accounts]
|
||||||
|
${cfg.accountName}: $password
|
||||||
|
|
||||||
|
${renderedVolumes}
|
||||||
|
EOF
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.modules.services.copyparty = {
|
||||||
|
enable = lib.mkEnableOption "Copyparty";
|
||||||
|
|
||||||
|
package = lib.mkPackageOption pkgs "copyparty-min" { };
|
||||||
|
|
||||||
|
port = lib.mkOption {
|
||||||
|
type = lib.types.port;
|
||||||
|
default = 3923;
|
||||||
|
description = "Port Copyparty listens on.";
|
||||||
|
};
|
||||||
|
|
||||||
|
user = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "copyparty";
|
||||||
|
description = "User account that runs Copyparty.";
|
||||||
|
};
|
||||||
|
|
||||||
|
uid = lib.mkOption {
|
||||||
|
type = lib.types.ints.positive;
|
||||||
|
default = my.serviceUid "copyparty";
|
||||||
|
description = "Stable uid for the Copyparty service user.";
|
||||||
|
};
|
||||||
|
|
||||||
|
group = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "storage";
|
||||||
|
description = "Group account that owns Copyparty's writable state and served files.";
|
||||||
|
};
|
||||||
|
|
||||||
|
accountName = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "alexion";
|
||||||
|
description = "Copyparty account name configured for password authentication.";
|
||||||
|
};
|
||||||
|
|
||||||
|
passwordFile = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
description = "Runtime file containing the Copyparty account password.";
|
||||||
|
};
|
||||||
|
|
||||||
|
stateDir = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "/var/lib/copyparty";
|
||||||
|
description = "Directory where Copyparty stores service state.";
|
||||||
|
};
|
||||||
|
|
||||||
|
volumes = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf volumeType;
|
||||||
|
default = { };
|
||||||
|
description = "Copyparty volumes keyed by their URL path.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = cfg.volumes != { };
|
||||||
|
message = "modules.services.copyparty.volumes must declare at least one served volume.";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
users.users.${cfg.user} = {
|
||||||
|
isSystemUser = true;
|
||||||
|
uid = cfg.uid;
|
||||||
|
group = cfg.group;
|
||||||
|
home = cfg.stateDir;
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.copyparty = {
|
||||||
|
description = "Copyparty file server";
|
||||||
|
after = [ "network.target" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
ReloadSignal = "SIGUSR1";
|
||||||
|
Type = "simple";
|
||||||
|
User = cfg.user;
|
||||||
|
Group = cfg.group;
|
||||||
|
WorkingDirectory = cfg.stateDir;
|
||||||
|
Environment = [
|
||||||
|
"HOME=${cfg.stateDir}"
|
||||||
|
"XDG_CONFIG_HOME=${cfg.stateDir}"
|
||||||
|
];
|
||||||
|
StateDirectory = "copyparty";
|
||||||
|
RuntimeDirectory = "copyparty";
|
||||||
|
RuntimeDirectoryMode = "0700";
|
||||||
|
LoadCredential = [ "password:${cfg.passwordFile}" ];
|
||||||
|
ExecStartPre = configScript;
|
||||||
|
ExecStart = "${lib.getExe cfg.package} -c /run/copyparty/copyparty.conf";
|
||||||
|
Restart = "on-failure";
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
PrivateTmp = true;
|
||||||
|
ProtectSystem = "strict";
|
||||||
|
ProtectHome = true;
|
||||||
|
ReadWritePaths = [ cfg.stateDir ] ++ map (volume: volume.path) (lib.attrValues cfg.volumes);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = [ cfg.port ];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ services:
|
|||||||
copyparty:
|
copyparty:
|
||||||
users:
|
users:
|
||||||
alexion:
|
alexion:
|
||||||
password: ENC[AES256_GCM,data:N7lCHbddcbRy757DbQwudIzcfWR8kDfiYgE/xBgrG1YbG1yBHWQ+IA2kVguTBH4m17N8M03xQfpcO86UBnuNFSGyrNFAUE+oDUQ=,iv:WTwNOkZSitPXW8QccOlEFw6K2ArQCCu5dq82ZsB/vfM=,tag:dM7hrFuKIfv6fpC0U5/tVw==,type:str]
|
password: ENC[AES256_GCM,data:SSyo2l6tCjgjxIuCEREIxU+inw2WHtJJR0/IEKUqefFfuAdA5Nf+lH/jfPw=,iv:9225w1Yur3KL3dBr1ct1aPvDdVJ94O8q9qAUiRrj8Nc=,tag:qZgx4XieVq4BcxkTZc0KGQ==,type:str]
|
||||||
sops:
|
sops:
|
||||||
age:
|
age:
|
||||||
- enc: |
|
- enc: |
|
||||||
@@ -33,7 +33,7 @@ sops:
|
|||||||
QkVLBz5x+ShVun27nN3oz8And0qXfwgXojhM3yWnBSUa9CFBsbTOJQ==
|
QkVLBz5x+ShVun27nN3oz8And0qXfwgXojhM3yWnBSUa9CFBsbTOJQ==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
recipient: age1wf5s0n0tgt6ld2ysgu9dc67mj8ylwecgl4utzg7hqwy3kut9zyms7aglmh
|
recipient: age1wf5s0n0tgt6ld2ysgu9dc67mj8ylwecgl4utzg7hqwy3kut9zyms7aglmh
|
||||||
lastmodified: "2026-08-16T18:23:31Z"
|
lastmodified: "2026-08-17T00:30:50Z"
|
||||||
mac: ENC[AES256_GCM,data:qgJpUdB3ZCck9Aa+QBOHdGNq88VXL7hrEjgRYvKC0GbOGTbCOBuMk8dYGjKbi8HCPdhWvhzLrlmnrg2/wciHfcgmQKx8S9RfkoorUV/jp4YYhueYkGMqN9ol9+Jvlknv9BUQT6jmsFesz8DT/+inl2lO1JxGW1LZ3nYLU7hSN6k=,iv:1GFyAKMYV2s3HI3MiyNpAqMIN2KaCeBLNljPtnLNn1E=,tag:jRb9NB7VgdJkyJG5osfqaw==,type:str]
|
mac: ENC[AES256_GCM,data:bNOsakxhrlO1quwLGA8inruHZ6tFpUo6cKMlV10jkJ75GOFzl5jzflGIuPfyncJCtIaZRBA2tS17TpGEEJozmbB2xQkeu67NmzOpEdoJzZWs/bY0FslbpGf8aGAd15BjOYNyMMNFavt0anRgEnsYXTDCflDCHLg1wvAvwtgRGYs=,iv:kfpzzWn3BHkSTQ8qpcD9lYn4wnZ/3nj/41SR25vUahE=,tag:8D1LSEBJIQcwYK9ETomwIw==,type:str]
|
||||||
unencrypted_suffix: _unencrypted
|
unencrypted_suffix: _unencrypted
|
||||||
version: 3.13.3
|
version: 3.13.3
|
||||||
|
|||||||
Reference in New Issue
Block a user