@highstate/library 0.18.0 → 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 +23 -4
  7. package/src/common/files.ts +27 -17
  8. package/src/common/server.ts +28 -7
  9. package/src/databases/etcd.ts +142 -37
  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 -40
  14. package/src/databases/mysql.ts +172 -0
  15. package/src/databases/postgresql.ts +133 -39
  16. package/src/databases/redis.ts +118 -43
  17. package/src/databases/s3.ts +49 -126
  18. package/src/dns.ts +12 -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 +17 -1
  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 +50 -9
  41. package/src/k8s/service.ts +7 -2
  42. package/src/k8s/shared.ts +50 -18
  43. package/src/k8s/workload.ts +65 -42
  44. package/src/network/address-space.ts +17 -7
  45. package/src/network/address.ts +14 -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 +23 -39
  49. package/src/network/index.ts +1 -1
  50. package/src/network/subnet.ts +5 -2
  51. package/src/proxmox.ts +27 -7
  52. package/src/restic.ts +9 -2
  53. package/src/ssh.ts +63 -31
  54. package/src/talos.ts +10 -1
  55. package/src/third-party/cloudflare.ts +4 -4
  56. package/src/third-party/timeweb.ts +18 -3
  57. package/src/third-party/yandex.ts +203 -86
  58. package/src/tls.ts +22 -0
  59. package/src/utils.ts +16 -1
  60. package/src/wireguard.ts +352 -120
  61. package/src/databases/mariadb.ts +0 -90
  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 -39
package/src/wireguard.ts CHANGED
@@ -4,13 +4,16 @@ import {
4
4
  $outputs,
5
5
  defineEntity,
6
6
  defineUnit,
7
+ type EntityInput,
8
+ type EntityValue,
7
9
  genericNameSchema,
10
+ secretSchema,
8
11
  z,
9
12
  } from "@highstate/contract"
10
13
  import { pick } from "remeda"
11
14
  import { fileEntity } from "./common"
12
15
  import { serverEntity } from "./common/server"
13
- import { etcdEntity } from "./databases"
16
+ import { etcd } from "./databases"
14
17
  import { clusterEntity, networkInterfaceEntity, workloadEntity } from "./k8s"
15
18
  import {
16
19
  addressEntity,
@@ -24,14 +27,37 @@ import { toPatchArgs } from "./utils"
24
27
  export const backendSchema = z.enum(["wireguard", "amneziawg"])
25
28
 
26
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
+ })
27
53
 
28
- const networkArgs = {
54
+ export const networkArgs = {
29
55
  /**
30
56
  * The backend to use for the WireGuard network.
31
57
  *
32
58
  * Possible values are:
33
59
  * - `wireguard` - the default backend;
34
- * - `amneziawg` - the censorship-resistant fork of WireGuard (NOT SUPPORTED YET).
60
+ * - `amneziawg` - the censorship-resistant fork of WireGuard.
35
61
  */
36
62
  backend: backendSchema.default("wireguard"),
37
63
 
@@ -50,8 +76,31 @@ const networkArgs = {
50
76
  * By default, IPv6 support is disabled.
51
77
  */
52
78
  ipv6: z.boolean().default(false),
79
+
80
+ /**
81
+ * The extra parameters for the amneziawg backend.
82
+ */
83
+ amnezia: amneziaParametersSchema.optional(),
53
84
  }
54
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
+
55
104
  /**
56
105
  * The entity representing the WireGuard network configuration.
57
106
  *
@@ -61,47 +110,139 @@ export const networkEntity = defineEntity({
61
110
  type: "wireguard.network.v1",
62
111
 
63
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
+ },
64
121
  })
65
122
 
66
123
  export const nodeExposePolicySchema = z.enum(["always", "when-has-endpoint", "never"])
67
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
+
68
176
  export const peerEntity = defineEntity({
69
177
  type: "wireguard.peer.v1",
70
178
 
71
179
  includes: {
72
180
  /**
73
- * 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.
74
184
  */
75
- endpoints: {
76
- 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,
77
195
  multiple: true,
196
+ required: false,
78
197
  },
79
- },
80
198
 
81
- schema: z.object({
82
199
  /**
83
- * The name of the WireGuard peer.
200
+ * The list of DNS servers to setup for the interface connected to the WireGuard peer.
84
201
  */
85
- name: genericNameSchema,
202
+ dns: {
203
+ entity: addressEntity,
204
+ multiple: true,
205
+ required: false,
206
+ },
86
207
 
87
208
  /**
88
- * The network to which the WireGuard peer belongs.
209
+ * The allowed subnets of the WireGuard peer.
89
210
  *
90
- * Holds shared configuration for all identities, peers, and nodes.
211
+ * Will be used to configure the `AllowedIPs` of the peer.
91
212
  */
92
- network: networkEntity.schema.optional(),
213
+ allowedSubnets: {
214
+ entity: subnetEntity,
215
+ multiple: true,
216
+ required: false,
217
+ },
93
218
 
94
219
  /**
95
- * The addresses of the WireGuard interface.
220
+ * The endpoints where the WireGuard peer can be reached.
96
221
  */
97
- addresses: addressEntity.schema.array(),
222
+ endpoints: {
223
+ entity: l4EndpointEntity,
224
+ multiple: true,
225
+ required: false,
226
+ },
98
227
 
99
228
  /**
100
- * The allowed subnets of the WireGuard peer.
229
+ * The peers which are relayed through this peer.
101
230
  *
102
- * 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.
103
233
  */
104
- 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,
105
246
 
106
247
  /**
107
248
  * The public key of the WireGuard peer.
@@ -115,19 +256,14 @@ export const peerEntity = defineEntity({
115
256
  *
116
257
  * Will be ignored if both peers have `presharedKeyPart` set.
117
258
  */
118
- presharedKey: z.string().optional(),
259
+ presharedKey: secretSchema(z.string()).optional(),
119
260
 
120
261
  /**
121
262
  * The pre-shared key part of the WireGuard peer.
122
263
  *
123
264
  * If both peers have `presharedKeyPart` set, their `presharedKey` will be calculated as sha256 of the two parts.
124
265
  */
125
- presharedKeyPart: z.string().optional(),
126
-
127
- /**
128
- * The list of DNS servers to setup for the interface connected to the WireGuard peer.
129
- */
130
- dns: addressEntity.schema.array(),
266
+ presharedKeyPart: secretSchema(z.string()).optional(),
131
267
 
132
268
  /**
133
269
  * The port where the WireGuard peer is listening.
@@ -146,18 +282,18 @@ export const peerEntity = defineEntity({
146
282
  persistentKeepalive: z.number().int().nonnegative().default(0),
147
283
 
148
284
  /**
149
- * The peers which are relayed through this peer.
285
+ * The wg-feed metadata of the WireGuard peer.
150
286
  *
151
- * All their allowed IPs will be added to this peer's allowed IPs
152
- * 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).
153
288
  */
154
- get relayedPeers() {
155
- return peerEntity.schema.array().optional()
156
- },
289
+ feedMetadata: feedMetadataSchema.optional(),
157
290
  }),
158
291
 
159
292
  meta: {
160
293
  color: "#673AB7",
294
+ icon: "simple-icons:wireguard",
295
+ secondaryIcon: "mdi:badge-account-horizontal",
296
+ iconColor: "#88171a",
161
297
  },
162
298
  })
163
299
 
@@ -175,50 +311,17 @@ export const identityEntity = defineEntity({
175
311
  /**
176
312
  * The private key of the WireGuard identity.
177
313
  */
178
- privateKey: z.string(),
314
+ privateKey: secretSchema(z.string()),
179
315
  }),
180
316
 
181
317
  meta: {
182
318
  color: "#F44336",
319
+ icon: "simple-icons:wireguard",
320
+ secondaryIcon: "mdi:account",
321
+ iconColor: "#88171a",
183
322
  },
184
323
  })
185
324
 
186
- export const feedDisplayInfoSchema = z.object({
187
- /**
188
- * The display title of the tunnel.
189
- */
190
- title: z.string(),
191
-
192
- /**
193
- * The display description of the tunnel.
194
- */
195
- description: z.string().optional(),
196
-
197
- /**
198
- * The display icon URL of the tunnel.
199
- *
200
- * Must only be `data:` URL with SVG image.
201
- */
202
- iconUrl: z.url().optional(),
203
- })
204
-
205
- export const feedMetadataSchema = z.object({
206
- /**
207
- * The ID of the tunnel in the feed.
208
- */
209
- id: z.string(),
210
-
211
- /**
212
- * The suggested name of the interface for the tunnel.
213
- */
214
- name: genericNameSchema,
215
-
216
- /**
217
- * The display information of the tunnel.
218
- */
219
- displayInfo: feedDisplayInfoSchema,
220
- })
221
-
222
325
  export const configEntity = defineEntity({
223
326
  type: "wireguard.config.v1",
224
327
 
@@ -237,13 +340,29 @@ export const configEntity = defineEntity({
237
340
  */
238
341
  feedMetadata: feedMetadataSchema.optional(),
239
342
  }),
343
+
344
+ meta: {
345
+ color: "#455A64",
346
+ title: "WireGuard Config",
347
+ icon: "simple-icons:wireguard",
348
+ iconColor: "#88171a",
349
+ secondaryIcon: "mdi:settings",
350
+ },
240
351
  })
241
352
 
242
- export type Network = z.infer<typeof networkEntity.schema>
243
- export type Identity = z.infer<typeof identityEntity.schema>
244
- 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>
245
356
  export type NodeExposePolicy = z.infer<typeof nodeExposePolicySchema>
246
- export type Config = z.infer<typeof configEntity.schema>
357
+ export type Config = EntityValue<typeof configEntity>
358
+
359
+ export type NetworkInput = EntityInput<typeof networkEntity>
360
+ export type IdentityInput = EntityInput<typeof identityEntity>
361
+ export type PeerInput = EntityInput<typeof peerEntity>
362
+ export type ConfigInput = EntityInput<typeof configEntity>
363
+
364
+ export type FeedDisplayInfo = z.infer<typeof feedDisplayInfoSchema>
365
+ export type FeedMetadata = z.infer<typeof feedMetadataSchema>
247
366
 
248
367
  /**
249
368
  * Holds the shared configuration for WireGuard identities, peers, and nodes.
@@ -270,6 +389,35 @@ export const network = defineUnit({
270
389
  },
271
390
  })
272
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
+
273
421
  const sharedPeerArgs = $args({
274
422
  /**
275
423
  * The name of the WireGuard peer.
@@ -357,6 +505,8 @@ const sharedPeerArgs = $args({
357
505
  * If set to 0, keepalive is disabled.
358
506
  */
359
507
  persistentKeepalive: z.number().int().nonnegative().default(0),
508
+
509
+ ...sharedFeedArgs,
360
510
  })
361
511
 
362
512
  const sharedPeerInputs = $inputs({
@@ -594,7 +744,12 @@ export const nodeK8s = defineUnit({
594
744
  required: false,
595
745
  },
596
746
 
597
- interface: {
747
+ downstreamInterface: {
748
+ entity: networkInterfaceEntity,
749
+ required: false,
750
+ },
751
+
752
+ fallbackInterface: {
598
753
  entity: networkInterfaceEntity,
599
754
  required: false,
600
755
  },
@@ -634,6 +789,120 @@ export const nodeK8s = defineUnit({
634
789
  },
635
790
  })
636
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
+
637
906
  /**
638
907
  * The WireGuard node deployed on a server using wg-quick systemd service.
639
908
  */
@@ -665,6 +934,8 @@ export const node = defineUnit({
665
934
  */
666
935
  enableMasquerade: z.boolean().default(true),
667
936
 
937
+ ...configSharedArgs,
938
+
668
939
  /**
669
940
  * Script to run before bringing up the interface.
670
941
  */
@@ -705,12 +976,6 @@ export const node = defineUnit({
705
976
  entity: peerEntity,
706
977
  required: false,
707
978
  },
708
-
709
- endpoints: {
710
- entity: l4EndpointEntity,
711
- required: false,
712
- multiple: true,
713
- },
714
979
  },
715
980
 
716
981
  meta: {
@@ -727,17 +992,6 @@ export const node = defineUnit({
727
992
  },
728
993
  })
729
994
 
730
- const sharedArgs = $args({
731
- /**
732
- * The filter to use when selecting endpoints for each peer.
733
- *
734
- * The first matching endpoint will be used.
735
- *
736
- * If not provided, all endpoints will be considered.
737
- */
738
- peerEndpointFilter: z.string().optional().meta({ language: "javascript" }),
739
- })
740
-
741
995
  /**
742
996
  * Just the WireGuard configuration for the identity and peers.
743
997
  */
@@ -745,34 +999,8 @@ export const config = defineUnit({
745
999
  type: "wireguard.config.v1",
746
1000
 
747
1001
  args: {
748
- ...sharedArgs,
749
-
750
- /**
751
- * The metadata to include in the wg-feed for this config.
752
- */
753
- feedMetadata: z
754
- .discriminatedUnion("enabled", [
755
- z.object({
756
- /**
757
- * Whether this config is enabled for upload to wg-feed.
758
- *
759
- * You must fill the metadata fields.
760
- */
761
- enabled: z.literal("true"),
762
-
763
- ...pick(feedMetadataSchema.shape, ["id", "name"]),
764
-
765
- // Highstate does not support nested objects in UI
766
- ...feedDisplayInfoSchema.shape,
767
- }),
768
- z.object({
769
- /**
770
- * Whether this config is enabled for upload to wg-feed.
771
- */
772
- enabled: z.literal("false"),
773
- }),
774
- ])
775
- .prefault({ enabled: "false" }),
1002
+ ...configSharedArgs,
1003
+ ...sharedFeedArgs,
776
1004
  },
777
1005
 
778
1006
  inputs: {
@@ -782,6 +1010,10 @@ export const config = defineUnit({
782
1010
  multiple: true,
783
1011
  required: false,
784
1012
  },
1013
+ network: {
1014
+ entity: networkEntity,
1015
+ required: false,
1016
+ },
785
1017
  },
786
1018
 
787
1019
  outputs: {
@@ -809,7 +1041,7 @@ export const configBundle = defineUnit({
809
1041
  type: "wireguard.config-bundle.v1",
810
1042
 
811
1043
  args: {
812
- ...sharedArgs,
1044
+ ...configSharedArgs,
813
1045
  },
814
1046
 
815
1047
  inputs: {
@@ -903,7 +1135,7 @@ export const feed = defineUnit({
903
1135
  },
904
1136
 
905
1137
  inputs: {
906
- etcd: etcdEntity,
1138
+ etcd: etcd.connectionEntity,
907
1139
  serverEndpoints: {
908
1140
  entity: l4EndpointEntity,
909
1141
  required: false,