ALPHA: wire is a tool to deploy nixos systems
wire.althaea.zone/
1# SPDX-License-Identifier: AGPL-3.0-or-later
2# Copyright 2024-2025 wire Contributors
3
4{
5 lib,
6 name,
7 ...
8}:
9let
10 inherit (lib) types;
11in
12{
13 imports =
14 let
15 inherit (lib) mkAliasOptionModule;
16 in
17 [
18 (mkAliasOptionModule [ "deployment" "targetHost" ] [ "deployment" "target" "hosts" ])
19 (mkAliasOptionModule [ "deployment" "targetUser" ] [ "deployment" "target" "user" ])
20 (mkAliasOptionModule [ "deployment" "targetPort" ] [ "deployment" "target" "port" ])
21 ];
22
23 options.deployment = {
24 target = lib.mkOption {
25 type = types.submodule {
26 imports = [
27 (lib.mkAliasOptionModule [ "host" ] [ "hosts" ])
28 ];
29 options = {
30 hosts = lib.mkOption {
31 type = types.coercedTo types.str lib.singleton (types.listOf types.str);
32 description = "IPs or hostnames to attempt to connect to. They are tried in order.";
33 default = lib.singleton name;
34 apply = lib.unique;
35 };
36 user = lib.mkOption {
37 type = types.str;
38 description = "User to use for SSH. The user must be atleast `wheel` and must use an SSH key or similar
39 non-interactive login method. More information can be found at https://wire.althaea.zone/guides/non-root-user";
40 default = "root";
41 };
42 port = lib.mkOption {
43 type = types.int;
44 default = 22;
45 description = "SSH port to use.";
46 };
47 };
48 };
49 description = "Describes the target for this node";
50 default = { };
51 };
52
53 buildOnTarget = lib.mkOption {
54 type = types.bool;
55 default = false;
56 description = "Whether to build the system on the target host or not.";
57 };
58
59 allowLocalDeployment = lib.mkOption {
60 type = types.bool;
61 default = true;
62 description = "Whether to allow or deny this node being applied to localhost when the host's hostname matches the
63 node's name.";
64 };
65
66 tags = lib.mkOption {
67 type = types.listOf types.str;
68 default = [ ];
69 description = "Tags for node.";
70 example = [
71 "arm"
72 "cloud"
73 ];
74 };
75
76 privilegeEscalationCommand = lib.mkOption {
77 type = types.listOf types.str;
78 description = "Command to elevate.";
79 default = [
80 "sudo"
81 "--"
82 ];
83 };
84
85 replaceUnknownProfiles = lib.mkOption {
86 type = types.bool;
87 description = "No-op, colmena compatibility";
88 default = true;
89 };
90
91 sshOptions = lib.mkOption {
92 type = types.listOf types.str;
93 description = "No-op, colmena compatibility";
94 default = [ ];
95 };
96
97 _keys = lib.mkOption {
98 internal = true;
99 readOnly = true;
100 };
101
102 _hostPlatform = lib.mkOption {
103 internal = true;
104 readOnly = true;
105 };
106
107 keys = lib.mkOption {
108 type = types.attrsOf (
109 types.submodule (
110 {
111 name,
112 config,
113 ...
114 }:
115 {
116 imports =
117 let
118 inherit (lib) mkAliasOptionModule;
119 in
120 [
121 (mkAliasOptionModule [ "keyFile" ] [ "source" ])
122 (mkAliasOptionModule [ "keyCommand" ] [ "source" ])
123 (mkAliasOptionModule [ "text" ] [ "source" ])
124 ];
125 options = {
126 name = lib.mkOption {
127 type = types.str;
128 default = name;
129 description = "Filename of the secret.";
130 };
131 destDir = lib.mkOption {
132 type = types.path;
133 default = "/run/keys/";
134 description = "Destination directory for the secret. Change this to something other than `/run/keys/` for keys to persist past reboots.";
135 };
136 path = lib.mkOption {
137 internal = true;
138 type = types.path;
139 default =
140 if lib.hasSuffix "/" config.destDir then
141 "${config.destDir}${config.name}"
142 else
143 "${config.destDir}/${config.name}";
144 description = "Path that the key is deployed to.";
145 };
146 service = lib.mkOption {
147 internal = true;
148 type = types.str;
149 default = "${config.name}-key.service";
150 description = "Name of the systemd service that represents this key.";
151 };
152 group = lib.mkOption {
153 type = types.str;
154 default = "root";
155 description = "Group to own the key. If this group does not exist this will silently fail and the key will be owned by gid 0.";
156 };
157 user = lib.mkOption {
158 type = types.str;
159 default = "root";
160 description = "User to own the key. If this user does not exist this will silently fail and the key will be owned by uid 0.";
161 };
162 permissions = lib.mkOption {
163 type = types.str;
164 default = "0600";
165 description = "Unix Octal permissions, in string format, for the key.";
166 };
167 source = lib.mkOption {
168 type = types.oneOf [
169 types.str
170 types.path
171 (types.listOf types.str)
172 ];
173 description = "Source of the key. Either a path to a file, a literal string, or a command to generate the key.";
174 };
175 uploadAt = lib.mkOption {
176 type = types.enum [
177 "pre-activation"
178 "post-activation"
179 ];
180 default = "pre-activation";
181 description = "When to upload the key. Either `pre-activation` or `post-activation`.";
182 };
183 environment = lib.mkOption {
184 type = types.attrsOf types.str;
185 default = { };
186 description = "Key-Value environment variables to use when creating the key if the key source is a command.";
187 };
188 };
189 }
190 )
191 );
192 description = "Secrets to be deployed to the node.";
193 default = { };
194 example = {
195 "wireless.env" = {
196 source = [
197 "gpg"
198 "--decrypt"
199 "secrets/wireless.env.gpg"
200 ];
201 destDir = "/etc/keys/";
202 };
203
204 "arbfile.txt" = {
205 source = ./arbfile.txt;
206 destDir = "/etc/arbs/";
207 };
208
209 "arberfile.txt" = {
210 source = ''
211 Hello World
212 '';
213 destDir = "/etc/arbs/";
214 };
215 };
216 };
217 };
218}