@highstate/library 0.19.1 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/highstate.library.msgpack +0 -0
  2. package/dist/index.js +2314 -1141
  3. package/dist/index.js.map +1 -1
  4. package/package.json +3 -3
  5. package/src/abbreviations.ts +3 -0
  6. package/src/common/access-point.ts +19 -4
  7. package/src/common/files.ts +24 -17
  8. package/src/common/server.ts +19 -6
  9. package/src/databases/etcd.ts +141 -39
  10. package/src/databases/index.ts +8 -7
  11. package/src/databases/influxdb3.ts +103 -0
  12. package/src/databases/minio.ts +157 -0
  13. package/src/databases/mongodb.ts +122 -41
  14. package/src/databases/mysql.ts +172 -0
  15. package/src/databases/postgresql.ts +133 -40
  16. package/src/databases/redis.ts +118 -44
  17. package/src/databases/s3.ts +48 -127
  18. package/src/dns.ts +11 -7
  19. package/src/index.ts +1 -1
  20. package/src/k3s.ts +7 -0
  21. package/src/k8s/apps/etcd.ts +3 -3
  22. package/src/k8s/apps/gitea.ts +1 -1
  23. package/src/k8s/apps/grafana.ts +48 -0
  24. package/src/k8s/apps/index.ts +4 -2
  25. package/src/k8s/apps/mariadb.ts +7 -38
  26. package/src/k8s/apps/matrix-stack.ts +62 -0
  27. package/src/k8s/apps/minio.ts +38 -45
  28. package/src/k8s/apps/mongodb.ts +6 -38
  29. package/src/k8s/apps/portal.ts +27 -0
  30. package/src/k8s/apps/postgresql.ts +7 -41
  31. package/src/k8s/apps/refluxdb.ts +49 -0
  32. package/src/k8s/apps/remnawave.ts +15 -2
  33. package/src/k8s/apps/shared.ts +16 -15
  34. package/src/k8s/apps/traefik.ts +5 -0
  35. package/src/k8s/apps/valkey.ts +5 -5
  36. package/src/k8s/apps/vaultwarden.ts +2 -6
  37. package/src/k8s/apps/workload.ts +14 -11
  38. package/src/k8s/cert-manager.ts +22 -0
  39. package/src/k8s/cilium.ts +17 -2
  40. package/src/k8s/resources.ts +41 -9
  41. package/src/k8s/service.ts +6 -2
  42. package/src/k8s/shared.ts +49 -18
  43. package/src/k8s/workload.ts +59 -43
  44. package/src/network/address-space.ts +16 -7
  45. package/src/network/address.ts +13 -2
  46. package/src/network/endpoint-container.ts +235 -0
  47. package/src/network/endpoint-schema.ts +8 -0
  48. package/src/network/endpoint.ts +20 -39
  49. package/src/network/index.ts +1 -1
  50. package/src/network/subnet.ts +4 -2
  51. package/src/proxmox.ts +24 -7
  52. package/src/restic.ts +8 -2
  53. package/src/ssh.ts +52 -30
  54. package/src/talos.ts +8 -2
  55. package/src/third-party/cloudflare.ts +4 -4
  56. package/src/third-party/timeweb.ts +17 -3
  57. package/src/third-party/yandex.ts +198 -88
  58. package/src/tls.ts +22 -0
  59. package/src/utils.ts +16 -1
  60. package/src/wireguard.ts +346 -120
  61. package/src/databases/mariadb.ts +0 -91
  62. package/src/databases/shared.ts +0 -67
  63. package/src/k8s/apps/kubernetes-dashboard.ts +0 -28
  64. package/src/k8s/apps/maybe.ts +0 -39
  65. package/src/network/dynamic-endpoint.ts +0 -43
package/src/wireguard.ts CHANGED
@@ -5,13 +5,15 @@ import {
5
5
  defineEntity,
6
6
  defineUnit,
7
7
  type EntityInput,
8
+ type EntityValue,
8
9
  genericNameSchema,
10
+ secretSchema,
9
11
  z,
10
12
  } from "@highstate/contract"
11
13
  import { pick } from "remeda"
12
14
  import { fileEntity } from "./common"
13
15
  import { serverEntity } from "./common/server"
14
- import { etcdEntity } from "./databases"
16
+ import { etcd } from "./databases"
15
17
  import { clusterEntity, networkInterfaceEntity, workloadEntity } from "./k8s"
16
18
  import {
17
19
  addressEntity,
@@ -25,14 +27,37 @@ import { toPatchArgs } from "./utils"
25
27
  export const backendSchema = z.enum(["wireguard", "amneziawg"])
26
28
 
27
29
  export type Backend = z.infer<typeof backendSchema>
30
+ export type AmneziaParameters = z.infer<typeof amneziaParametersSchema>
31
+
32
+ export const amneziaParametersSchema = z.object({
33
+ i1: z.string().optional(),
34
+ i2: z.string().optional(),
35
+ i3: z.string().optional(),
36
+ i4: z.string().optional(),
37
+ i5: z.string().optional(),
38
+
39
+ s1: z.number().optional(),
40
+ s2: z.number().optional(),
41
+ s3: z.number().optional(),
42
+ s4: z.number().optional(),
43
+
44
+ jc: z.number().optional(),
45
+ jmin: z.number().optional(),
46
+ jmax: z.number().optional(),
47
+
48
+ h1: z.string().optional(),
49
+ h2: z.string().optional(),
50
+ h3: z.string().optional(),
51
+ h4: z.string().optional(),
52
+ })
28
53
 
29
- const networkArgs = {
54
+ export const networkArgs = {
30
55
  /**
31
56
  * The backend to use for the WireGuard network.
32
57
  *
33
58
  * Possible values are:
34
59
  * - `wireguard` - the default backend;
35
- * - `amneziawg` - the censorship-resistant fork of WireGuard (NOT SUPPORTED YET).
60
+ * - `amneziawg` - the censorship-resistant fork of WireGuard.
36
61
  */
37
62
  backend: backendSchema.default("wireguard"),
38
63
 
@@ -51,8 +76,31 @@ const networkArgs = {
51
76
  * By default, IPv6 support is disabled.
52
77
  */
53
78
  ipv6: z.boolean().default(false),
79
+
80
+ /**
81
+ * The extra parameters for the amneziawg backend.
82
+ */
83
+ amnezia: amneziaParametersSchema.optional(),
54
84
  }
55
85
 
86
+ const configSharedArgs = $args({
87
+ /**
88
+ * The filter to use when selecting endpoints for each peer.
89
+ *
90
+ * The first matching endpoint will be used.
91
+ *
92
+ * If not provided, all endpoints will be considered.
93
+ */
94
+ peerEndpointFilter: z.string().optional().meta({ language: "javascript" }),
95
+
96
+ /**
97
+ * Whether to include the `ListenPort` in the generated config.
98
+ *
99
+ * By default, is `false` because in most cases the config is used for peers which don't listen for incoming connections.
100
+ */
101
+ listen: z.boolean().default(false),
102
+ })
103
+
56
104
  /**
57
105
  * The entity representing the WireGuard network configuration.
58
106
  *
@@ -62,47 +110,139 @@ export const networkEntity = defineEntity({
62
110
  type: "wireguard.network.v1",
63
111
 
64
112
  schema: z.object(networkArgs),
113
+
114
+ meta: {
115
+ color: "#88171a",
116
+ title: "WireGuard Network",
117
+ icon: "simple-icons:wireguard",
118
+ iconColor: "#88171a",
119
+ secondaryIcon: "mdi:local-area-network-connect",
120
+ },
65
121
  })
66
122
 
67
123
  export const nodeExposePolicySchema = z.enum(["always", "when-has-endpoint", "never"])
68
124
 
125
+ export const feedDisplayInfoSchema = z.object({
126
+ /**
127
+ * The display title of the tunnel.
128
+ */
129
+ title: z.string(),
130
+
131
+ /**
132
+ * The display description of the tunnel.
133
+ */
134
+ description: z.string().optional(),
135
+
136
+ /**
137
+ * The display icon URL of the tunnel.
138
+ *
139
+ * Must only be `data:` URL with SVG image.
140
+ */
141
+ iconUrl: z.url().optional(),
142
+ })
143
+
144
+ export const feedMetadataSchema = z.object({
145
+ /**
146
+ * The ID of the tunnel in the feed.
147
+ */
148
+ id: z.string(),
149
+
150
+ /**
151
+ * The suggested name of the interface for the tunnel.
152
+ */
153
+ name: genericNameSchema,
154
+
155
+ /**
156
+ * Whether the tunnel should be enabled by the client.
157
+ */
158
+ enabled: z.boolean().default(false),
159
+
160
+ /**
161
+ * Whether the tunnel state must be forced by the client.
162
+ */
163
+ forced: z.boolean().default(false),
164
+
165
+ /**
166
+ * Whether the tunnel is exclusive and should not be enabled with other tunnels.
167
+ */
168
+ exclusive: z.boolean().default(false),
169
+
170
+ /**
171
+ * The display information of the tunnel.
172
+ */
173
+ displayInfo: feedDisplayInfoSchema,
174
+ })
175
+
69
176
  export const peerEntity = defineEntity({
70
177
  type: "wireguard.peer.v1",
71
178
 
72
179
  includes: {
73
180
  /**
74
- * The endpoints where the WireGuard peer can be reached.
181
+ * The network to which the WireGuard peer belongs.
182
+ *
183
+ * Holds shared configuration for all identities, peers, and nodes.
75
184
  */
76
- endpoints: {
77
- entity: l4EndpointEntity,
185
+ network: {
186
+ entity: networkEntity,
187
+ required: false,
188
+ },
189
+
190
+ /**
191
+ * The addresses of the WireGuard interface.
192
+ */
193
+ addresses: {
194
+ entity: addressEntity,
78
195
  multiple: true,
196
+ required: false,
79
197
  },
80
- },
81
198
 
82
- schema: z.object({
83
199
  /**
84
- * The name of the WireGuard peer.
200
+ * The list of DNS servers to setup for the interface connected to the WireGuard peer.
85
201
  */
86
- name: genericNameSchema,
202
+ dns: {
203
+ entity: addressEntity,
204
+ multiple: true,
205
+ required: false,
206
+ },
87
207
 
88
208
  /**
89
- * The network to which the WireGuard peer belongs.
209
+ * The allowed subnets of the WireGuard peer.
90
210
  *
91
- * Holds shared configuration for all identities, peers, and nodes.
211
+ * Will be used to configure the `AllowedIPs` of the peer.
92
212
  */
93
- network: networkEntity.schema.optional(),
213
+ allowedSubnets: {
214
+ entity: subnetEntity,
215
+ multiple: true,
216
+ required: false,
217
+ },
94
218
 
95
219
  /**
96
- * The addresses of the WireGuard interface.
220
+ * The endpoints where the WireGuard peer can be reached.
97
221
  */
98
- addresses: addressEntity.schema.array(),
222
+ endpoints: {
223
+ entity: l4EndpointEntity,
224
+ multiple: true,
225
+ required: false,
226
+ },
99
227
 
100
228
  /**
101
- * The allowed subnets of the WireGuard peer.
229
+ * The peers which are relayed through this peer.
102
230
  *
103
- * Will be used to configure the `AllowedIPs` of the peer.
231
+ * All their allowed IPs will be added to this peer's allowed IPs
232
+ * and will be used to setup routing for all other peers except the relayed ones.
104
233
  */
105
- allowedSubnets: subnetEntity.schema.array(),
234
+ relayedPeers: {
235
+ entity: () => peerEntity,
236
+ multiple: true,
237
+ required: false,
238
+ },
239
+ },
240
+
241
+ schema: z.object({
242
+ /**
243
+ * The name of the WireGuard peer.
244
+ */
245
+ name: genericNameSchema,
106
246
 
107
247
  /**
108
248
  * The public key of the WireGuard peer.
@@ -116,19 +256,14 @@ export const peerEntity = defineEntity({
116
256
  *
117
257
  * Will be ignored if both peers have `presharedKeyPart` set.
118
258
  */
119
- presharedKey: z.string().optional(),
259
+ presharedKey: secretSchema(z.string()).optional(),
120
260
 
121
261
  /**
122
262
  * The pre-shared key part of the WireGuard peer.
123
263
  *
124
264
  * If both peers have `presharedKeyPart` set, their `presharedKey` will be calculated as sha256 of the two parts.
125
265
  */
126
- presharedKeyPart: z.string().optional(),
127
-
128
- /**
129
- * The list of DNS servers to setup for the interface connected to the WireGuard peer.
130
- */
131
- dns: addressEntity.schema.array(),
266
+ presharedKeyPart: secretSchema(z.string()).optional(),
132
267
 
133
268
  /**
134
269
  * The port where the WireGuard peer is listening.
@@ -147,18 +282,18 @@ export const peerEntity = defineEntity({
147
282
  persistentKeepalive: z.number().int().nonnegative().default(0),
148
283
 
149
284
  /**
150
- * The peers which are relayed through this peer.
285
+ * The wg-feed metadata of the WireGuard peer.
151
286
  *
152
- * All their allowed IPs will be added to this peer's allowed IPs
153
- * and will be used to setup routing for all other peers except the relayed ones.
287
+ * Will be used in the config referencing this peer (not the peer itself).
154
288
  */
155
- get relayedPeers() {
156
- return peerEntity.schema.array().optional()
157
- },
289
+ feedMetadata: feedMetadataSchema.optional(),
158
290
  }),
159
291
 
160
292
  meta: {
161
293
  color: "#673AB7",
294
+ icon: "simple-icons:wireguard",
295
+ secondaryIcon: "mdi:badge-account-horizontal",
296
+ iconColor: "#88171a",
162
297
  },
163
298
  })
164
299
 
@@ -176,50 +311,17 @@ export const identityEntity = defineEntity({
176
311
  /**
177
312
  * The private key of the WireGuard identity.
178
313
  */
179
- privateKey: z.string(),
314
+ privateKey: secretSchema(z.string()),
180
315
  }),
181
316
 
182
317
  meta: {
183
318
  color: "#F44336",
319
+ icon: "simple-icons:wireguard",
320
+ secondaryIcon: "mdi:account",
321
+ iconColor: "#88171a",
184
322
  },
185
323
  })
186
324
 
187
- export const feedDisplayInfoSchema = z.object({
188
- /**
189
- * The display title of the tunnel.
190
- */
191
- title: z.string(),
192
-
193
- /**
194
- * The display description of the tunnel.
195
- */
196
- description: z.string().optional(),
197
-
198
- /**
199
- * The display icon URL of the tunnel.
200
- *
201
- * Must only be `data:` URL with SVG image.
202
- */
203
- iconUrl: z.url().optional(),
204
- })
205
-
206
- export const feedMetadataSchema = z.object({
207
- /**
208
- * The ID of the tunnel in the feed.
209
- */
210
- id: z.string(),
211
-
212
- /**
213
- * The suggested name of the interface for the tunnel.
214
- */
215
- name: genericNameSchema,
216
-
217
- /**
218
- * The display information of the tunnel.
219
- */
220
- displayInfo: feedDisplayInfoSchema,
221
- })
222
-
223
325
  export const configEntity = defineEntity({
224
326
  type: "wireguard.config.v1",
225
327
 
@@ -238,19 +340,30 @@ export const configEntity = defineEntity({
238
340
  */
239
341
  feedMetadata: feedMetadataSchema.optional(),
240
342
  }),
343
+
344
+ meta: {
345
+ color: "#455A64",
346
+ title: "WireGuard Config",
347
+ icon: "simple-icons:wireguard",
348
+ iconColor: "#88171a",
349
+ secondaryIcon: "mdi:settings",
350
+ },
241
351
  })
242
352
 
243
- export type Network = z.infer<typeof networkEntity.schema>
244
- export type Identity = z.infer<typeof identityEntity.schema>
245
- export type Peer = z.infer<typeof peerEntity.schema>
353
+ export type Network = EntityValue<typeof networkEntity>
354
+ export type Identity = EntityValue<typeof identityEntity>
355
+ export type Peer = EntityValue<typeof peerEntity>
246
356
  export type NodeExposePolicy = z.infer<typeof nodeExposePolicySchema>
247
- export type Config = z.infer<typeof configEntity.schema>
357
+ export type Config = EntityValue<typeof configEntity>
248
358
 
249
359
  export type NetworkInput = EntityInput<typeof networkEntity>
250
360
  export type IdentityInput = EntityInput<typeof identityEntity>
251
361
  export type PeerInput = EntityInput<typeof peerEntity>
252
362
  export type ConfigInput = EntityInput<typeof configEntity>
253
363
 
364
+ export type FeedDisplayInfo = z.infer<typeof feedDisplayInfoSchema>
365
+ export type FeedMetadata = z.infer<typeof feedMetadataSchema>
366
+
254
367
  /**
255
368
  * Holds the shared configuration for WireGuard identities, peers, and nodes.
256
369
  */
@@ -276,6 +389,35 @@ export const network = defineUnit({
276
389
  },
277
390
  })
278
391
 
392
+ const sharedFeedArgs = $args({
393
+ /**
394
+ * The metadata to include in the wg-feed for this config.
395
+ */
396
+ feedMetadata: z
397
+ .discriminatedUnion("provided", [
398
+ z.object({
399
+ /**
400
+ * Whether this config is enabled for upload to wg-feed.
401
+ *
402
+ * You must fill the metadata fields.
403
+ */
404
+ provided: z.literal("yes"),
405
+
406
+ ...pick(feedMetadataSchema.shape, ["id", "name", "enabled", "forced", "exclusive"]),
407
+
408
+ // Highstate does not support nested objects in UI
409
+ ...feedDisplayInfoSchema.shape,
410
+ }),
411
+ z.object({
412
+ /**
413
+ * Whether this config is enabled for upload to wg-feed.
414
+ */
415
+ provided: z.literal("no"),
416
+ }),
417
+ ])
418
+ .prefault({ provided: "no" }),
419
+ })
420
+
279
421
  const sharedPeerArgs = $args({
280
422
  /**
281
423
  * The name of the WireGuard peer.
@@ -363,6 +505,8 @@ const sharedPeerArgs = $args({
363
505
  * If set to 0, keepalive is disabled.
364
506
  */
365
507
  persistentKeepalive: z.number().int().nonnegative().default(0),
508
+
509
+ ...sharedFeedArgs,
366
510
  })
367
511
 
368
512
  const sharedPeerInputs = $inputs({
@@ -600,7 +744,12 @@ export const nodeK8s = defineUnit({
600
744
  required: false,
601
745
  },
602
746
 
603
- interface: {
747
+ downstreamInterface: {
748
+ entity: networkInterfaceEntity,
749
+ required: false,
750
+ },
751
+
752
+ fallbackInterface: {
604
753
  entity: networkInterfaceEntity,
605
754
  required: false,
606
755
  },
@@ -640,6 +789,120 @@ export const nodeK8s = defineUnit({
640
789
  },
641
790
  })
642
791
 
792
+ /**
793
+ * The wg-feed daemon deployed in the Kubernetes cluster.
794
+ *
795
+ * It runs `wg-feed-daemon` which continuously reconciles local WireGuard tunnels
796
+ * from one or more wg-feed Setup URLs.
797
+ */
798
+ export const nodeFeedK8s = defineUnit({
799
+ type: "wireguard.node.feed.k8s.v1",
800
+
801
+ args: {
802
+ /**
803
+ * The name of the namespace/deployment/statefulset where the daemon will be deployed.
804
+ *
805
+ * By default, the name is `wg-${identity.name}`.
806
+ */
807
+ appName: z.string().optional(),
808
+
809
+ /**
810
+ * Whether to expose the WireGuard node to the outside world.
811
+ */
812
+ external: z.boolean().default(false),
813
+
814
+ /**
815
+ * Whether to create a Service and allow ingress.
816
+ */
817
+ expose: z.boolean().default(false),
818
+
819
+ /**
820
+ * The UDP port that will be exposed by the Service.
821
+ *
822
+ * This is required for consumers that need to know the port ahead of time.
823
+ */
824
+ listenPort: z.number().default(51820),
825
+
826
+ /**
827
+ * The name of the WireGuard interface to export as `interface` output.
828
+ *
829
+ * Note: wg-feed may manage multiple tunnels.
830
+ * This output is a convenience handle for downstream units.
831
+ */
832
+ interfaceName: z.string().default("wg0"),
833
+
834
+ /**
835
+ * List of CIDR blocks that should be blocked from forwarding through this WireGuard node.
836
+ *
837
+ * This prevents other peers from reaching these destination CIDRs while still allowing
838
+ * the peers in those CIDRs to access the internet and other allowed endpoints.
839
+ *
840
+ * Useful for peer isolation where you want to prevent cross-peer communication.
841
+ */
842
+ forwardRestrictedSubnets: z.string().array().default([]),
843
+
844
+ /**
845
+ * The extra specification of the container which runs the wg-feed daemon.
846
+ *
847
+ * Will override any overlapping fields.
848
+ */
849
+ containerSpec: z.record(z.string(), z.unknown()).optional(),
850
+ },
851
+
852
+ inputs: {
853
+ k8sCluster: clusterEntity,
854
+ endpoints: {
855
+ entity: l7EndpointEntity,
856
+ multiple: true,
857
+ required: false,
858
+ },
859
+
860
+ peer: {
861
+ entity: peerEntity,
862
+ required: false,
863
+ },
864
+
865
+ workload: {
866
+ entity: workloadEntity,
867
+ required: false,
868
+ },
869
+
870
+ interface: {
871
+ entity: networkInterfaceEntity,
872
+ required: false,
873
+ },
874
+ },
875
+
876
+ outputs: {
877
+ workload: {
878
+ entity: workloadEntity,
879
+ },
880
+
881
+ interface: {
882
+ entity: networkInterfaceEntity,
883
+ },
884
+
885
+ peer: {
886
+ entity: peerEntity,
887
+ required: false,
888
+ },
889
+ },
890
+
891
+ meta: {
892
+ title: "WireGuard Feed Kubernetes Node",
893
+ description: "The wg-feed daemon deployed in the Kubernetes cluster.",
894
+ icon: "simple-icons:wireguard",
895
+ iconColor: "#88171a",
896
+ secondaryIcon: "devicon:kubernetes",
897
+ category: "VPN",
898
+ },
899
+
900
+ source: {
901
+ package: "@highstate/wireguard",
902
+ path: "node.feed.k8s",
903
+ },
904
+ })
905
+
643
906
  /**
644
907
  * The WireGuard node deployed on a server using wg-quick systemd service.
645
908
  */
@@ -671,6 +934,8 @@ export const node = defineUnit({
671
934
  */
672
935
  enableMasquerade: z.boolean().default(true),
673
936
 
937
+ ...configSharedArgs,
938
+
674
939
  /**
675
940
  * Script to run before bringing up the interface.
676
941
  */
@@ -711,12 +976,6 @@ export const node = defineUnit({
711
976
  entity: peerEntity,
712
977
  required: false,
713
978
  },
714
-
715
- endpoints: {
716
- entity: l4EndpointEntity,
717
- required: false,
718
- multiple: true,
719
- },
720
979
  },
721
980
 
722
981
  meta: {
@@ -733,17 +992,6 @@ export const node = defineUnit({
733
992
  },
734
993
  })
735
994
 
736
- const sharedArgs = $args({
737
- /**
738
- * The filter to use when selecting endpoints for each peer.
739
- *
740
- * The first matching endpoint will be used.
741
- *
742
- * If not provided, all endpoints will be considered.
743
- */
744
- peerEndpointFilter: z.string().optional().meta({ language: "javascript" }),
745
- })
746
-
747
995
  /**
748
996
  * Just the WireGuard configuration for the identity and peers.
749
997
  */
@@ -751,34 +999,8 @@ export const config = defineUnit({
751
999
  type: "wireguard.config.v1",
752
1000
 
753
1001
  args: {
754
- ...sharedArgs,
755
-
756
- /**
757
- * The metadata to include in the wg-feed for this config.
758
- */
759
- feedMetadata: z
760
- .discriminatedUnion("enabled", [
761
- z.object({
762
- /**
763
- * Whether this config is enabled for upload to wg-feed.
764
- *
765
- * You must fill the metadata fields.
766
- */
767
- enabled: z.literal("true"),
768
-
769
- ...pick(feedMetadataSchema.shape, ["id", "name"]),
770
-
771
- // Highstate does not support nested objects in UI
772
- ...feedDisplayInfoSchema.shape,
773
- }),
774
- z.object({
775
- /**
776
- * Whether this config is enabled for upload to wg-feed.
777
- */
778
- enabled: z.literal("false"),
779
- }),
780
- ])
781
- .prefault({ enabled: "false" }),
1002
+ ...configSharedArgs,
1003
+ ...sharedFeedArgs,
782
1004
  },
783
1005
 
784
1006
  inputs: {
@@ -788,6 +1010,10 @@ export const config = defineUnit({
788
1010
  multiple: true,
789
1011
  required: false,
790
1012
  },
1013
+ network: {
1014
+ entity: networkEntity,
1015
+ required: false,
1016
+ },
791
1017
  },
792
1018
 
793
1019
  outputs: {
@@ -815,7 +1041,7 @@ export const configBundle = defineUnit({
815
1041
  type: "wireguard.config-bundle.v1",
816
1042
 
817
1043
  args: {
818
- ...sharedArgs,
1044
+ ...configSharedArgs,
819
1045
  },
820
1046
 
821
1047
  inputs: {
@@ -909,7 +1135,7 @@ export const feed = defineUnit({
909
1135
  },
910
1136
 
911
1137
  inputs: {
912
- etcd: etcdEntity,
1138
+ etcd: etcd.connectionEntity,
913
1139
  serverEndpoints: {
914
1140
  entity: l4EndpointEntity,
915
1141
  required: false,